Exclude input events in famous-angular from general Event - famo.us

I'm writing a mobile app with famous/angular. A swipe to the right in any view reveals the menu. For this I have set up the following event handling:
var MouseSync = $famous["famous/inputs/MouseSync"];
var TouchSync = $famous["famous/inputs/TouchSync"];
var GenericSync = $famous['famous/inputs/GenericSync'];
$scope.sync = new GenericSync(
["mouse", "touch"],
{direction: GenericSync.DIRECTION_X}
);
$scope.sync.on('update', function(data){
// do stuff while swiping
});
$scope.sync.on('end', function(data) {
// do stuff at swipe end
});
The above is all working fine. My problem now is that I have html inputs in some of the views which I cannot access/enter due to the above. The surfaces in which these are contained pipe their events to 'sync':
<fa-surface fa-pipe-to="sync">
<input type="text"></input>
</fa-surface>
I know that the issue here is that the click-event on my input is passed on to sync. I just don't know what to do about it. Any ideas?
Many thanks!

Did you try the HTML5 autofocus attribute <input type="text" autofocus></input> or setting the focus on click? <input type="text" ng-click="focus($event)"></input>
For the second option, you will need to set up a function on the scope as follows:
$scope.focus = function(ev){
ev.target.focus()
}

Related

sending actions from within a component in Ember

I'm trying to figure out how to handle in a single place a "login" action, that must be activated from a button click and "enter" keypress;
in my template I've wrapped everything inside a "login-functions" component
//template home.hbs
{{#login-functions}}
<input id="email" type="email" class="validate">
<input id="password" type="password" class="validate">
<div id="login" {{action "login"}}>
{{/login-functions}}
//component components/login-functions.js
export default Ember.Component.extend({
actions: {
login: function() {
var email = $('#email').val();
var password = $('#password').val();
var self = this;
Utils.login(email, password).done(function(res) {
localStorage.setItem('Access-Token', res.data.token);
localStorage.setItem('userId', res.data.user_id);
self.transitionToRoute('feed');
})
.error(function(error) {
//handle error
});
}
},
keyPress: function(event) {
if (event.charCode === 13) {
console.log('login attempt');
this.send('login');
}
}
});
But this doesn't work because the action is not sent from the template button to the component's action and also because from the component I can't perform the transition.
Can someone tell the best way to handle this?
I think it's because your button is part of your home template. Maybe the form should be moved in your login-functions template and be called from your home template by doing {{login-functions}}.
And for the redirection, components can't do them, I'd suggest to call an action using this.sendAction('didLogin') and handle the redirection in your HomeController.
Move the keyPress functions out of the actions.
Updated --- Here is a working demo

Inputs on Ember refresh page when a user hits Enter key

I'm using Ember CLI and have noticed odd behaviour. When the user clicks into the input and presses the enter key, the page refreshes.
My page has a basic element like this that is NOT part of any form:
<input type="text" class="form-control" id="per_page" value="50">
I am currently serving the page via:
ember cli
So node is hosting and has the fancy live reload thing going on so that when I update a page that is part of the underlying app.
So what is causing a page reload the enter key pressed inside an input? Could it be node or live reload? Are inputs just supposed to refresh a page when a user presses the enter key and I missed that in my HTML for dummies book?
**Better still, how can I intercept and instead call a function via:
{{action** "myFunction"}}
That happens because when you hit Enter, form gets submitted which results in page reload. what you need to do is set onsubmit="return false" on the form so nothing happens during submit. you can bind input to execute some action by adding action attribute action="doSomething"
<form onsubmit="return false">
{{input type="text" action="createComment" value=topic id="inputTopic"}}
</form>
Edit: In Ember 3+ you now use the {{on}} modifier to setup events on elements.
<form {{on 'submit' this.submitForm}}>
<!-- the rest of your form here -->
<button type='submit'>Submit</button>
</form>
And the action defined like so
#action
submitForm(event) {
event.preventDefault();
// Your code
}
Historically Ember has handled this use case with the following code:
<form {{action 'submitForm' on='submit'}}>
<!-- the rest of your form here -->
<button type='submit'>Submit</button>
</form>
This prevents the form from refreshing the page.
There is another method that gives you more control, by giving you the event so you can manage that yourself:
<form onsubmit={{action 'submitForm'}}>
<!-- the rest of your form here -->
<button type='submit'>Submit</button>
</form>
In this case, you will get an event and will have to call event.preventDefault() to stop the page refresh.
actions: {
submitForm(event) {
event.preventDefault();
}
}
This is a running example of the two: https://ember-twiddle.com/827820958e054f7af57b7677630729fc?openFiles=controllers.application.js%2C
I had the same problem - what worked for me, was to overwrite the keyPress Event in the input component like this:
keyPress: function (e) {
var keyCodeEnter = 13;
if (e.keyCode === keyCodeEnter) {
return false;
}
}
Hope it will help someone in the future! :)

How to make react.js play nice together with zurb reveal modal form

I am trying to integrate zurb reveal with form into react component. So far next code properly displays modal form:
ModalForm = React.createClass({
handleSubmit: function(attrs) {
this.props.onSubmit(attrs);
return false;
},
render: function(){
return(
<div>
Add new
<div id="formModal" className="reveal-modal" data-reveal>
<h4>Add something new</h4>
<Form onSubmit={this.handleSubmit} />
<a className="close-reveal-modal">×</a>
</div>
</div>
);
}
});
The Form component is pretty standard:
Form = React.createClass({
handleSubmit: function() {
var body = this.refs.body.getDOMNode().value.trim();
if (!body) {
return false;
}
this.props.onSubmit({body: body});
this.refs.body.getDOMNode().value = '';
return false;
},
render: function(){
return(
<form onSubmit={this.handleSubmit}>
<textarea name="body" placeholder="Say something..." ref="body" />
<input type="submit" value="Send" className="button" />
</form>
);
}
});
Problem: When I render form component within modal form component and enter something into form input then I see in console exception Uncaught object. This is a stack:
Uncaught object
invariant
ReactMount.findComponentRoot
ReactMount.findReactNodeByID
getNode
...
If I just render form component directly in the parent component then everything works. Could anybody help please?
In short, you're doing this wrong and this is not a bug in react.
If you use any kind of plugin that modifies the react component's dom nodes then it's going to break things in one way or another.
What you should be doing instead is using react itself, and complementary css, to position the component in the way you'd like for your modal dialog.
I would suggest creating a component that uses react's statics component property to define a couple of functions wrapping renderComponent to give you a nice clean function call to show or hide a react dialog. Here's a cut down example of something I've used in the past. NB: It does use jQuery but you could replace the jQ with standard js api calls to things like elementById and etc if you don't want the jQuery code.
window.MyDialog = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
content: React.PropTypes.string.isRequired
},
statics: {
// open a dialog with props object as props
open: function(props) {
var $anchor = $('#dialog-anchor');
if (!$anchor.length) {
$anchor = $('<div></div>')
.prop('id', 'dialog-anchor');
.appendTo('body');
}
return React.renderComponent(
MyDialog(props),
$anchor.get(0)
);
},
// close a dialog
close: function() {
React.unmountComponentAtNode($('#dialog-anchor').get(0));
}
},
// when dialog opens, add a keyup event handler to body
componentDidMount: function() {
$('body').on('keyup.myDialog', this.globalKeyupHandler);
},
// when dialog closes, clean up the bound keyup event handler on body
componentWillUnmount: function() {
$('body').off('keyup.myDialog');
},
// handles keyup events on body
globalKeyupHandler: function(e) {
if (e.keyCode == 27) { // ESC key
// close the dialog
this.statics.close();
}
},
// Extremely basic dialog dom layout - use your own
render: function() {
<div className="dialog">
<div className="title-bar">
<div className="title">{this.props.title}</div>
<a href="#" className="close" onClick={this.closeHandler}>
</div>
</div>
<div className="content">
{this.props.content}
</div>
</div>
}
});
You then open a dialog by calling:
MyDialog.open({title: 'Dialog Title', content: 'My dialog content'});
And close it with
MyDialog.close()
The dialog always attaches to a new dom node directly under body with id 'dialog-anchor'. If you open a dialog when one is already open, it will simply update the dom based on new props (or not if they're the same).
Of course passing the content of the dialog as a props argument isn't particularly useful. I usually extend below to either parse markdown -> html for the content or get some html via an ajax request inside the component when supplying a url as a prop instead.
I know the above code isn't exactly what you were looking for but I don't think there's a good way to make a dom-modifying plugin work with react. You can never assume that the dom representation of the react component is static and therefore it can't be manipulated by a 3rd party plugin successfully. I honestly think if you want to use react in this way you should re-evaluate why you're using the framework.
That said, I think the code above is a great starting point for a dialog in which all manipulation occurs inside the component, which afterall is what reactjs is all about!
NB: code was written very quickly from memory and not actually tested in it's current form so sorry if there are some minor syntax errors or something.
Here is how to do what Mike did, but using a zf reveal modal:
var Dialog = React.createClass({
statics: {
open: function(){
this.$dialog = $('#my-dialog');
if (!this.$dialog.length) {
this.$dialog = $('<div id="my-dialog" class="reveal-modal" data-reveal role="dialog"></div>')
.appendTo('body');
}
this.$dialog.foundation('reveal', 'open');
return React.render(
<Dialog close={this.close.bind(this)}/>,
this.$dialog[0]
);
},
close: function(){
if(!this.$dialog || !this.$dialog.length) {
return;
}
React.unmountComponentAtNode(this.$dialog[0]);
this.$dialog.foundation('reveal', 'close');
},
},
render : function() {
return (
<div>
<h1>This gets rendered into the modal</h1>
<a href="#" className="button" onClick={this.props.close}>Close</a>
</div>
);
}
});

{{action}} helper keyboard events don't work?

I somehow expected that to work:
<input {{action save this on="keyPress"}}>
But nothing happens.
However, changing it to a mouse event like that works perfectly:
<input {{action save this on="mouseDown"}}>
Am I misunderstanding something?
Note: If I add the keyPress()-hook to the corresponding view, everything works as expected.
The recommended way would be to use a custom view and handle the events in the view.
App.TextField = Em.TextField.extend({
keyUp: function(){ alert( 1 ); }
});
Then in the template you would use:
{{view App.TextField}}
My fix for this was just merged into Ember: https://github.com/emberjs/ember.js/commit/567aafa8f0de6b29d90d8638f2166b8df6275f9c

How to build a form with access to text input values on submit with ember.js

I'm using the latest pre 1.0 of ember.js and wanted to get away from using the deprecated button for simple forms.
I have something that works but I don't feel like this is the correct way to wire up a view that has both a text input and a button that needs access to that text.
Here is the basic view
{{#view PersonApp.AddPersonView}}
{{view Ember.TextField valueBinding="username"}}
{{#with this as username}}
<input type="submit" value="add" {{action addPerson username}}/>
{{/with}}
{{/view}}
Here is the view
PersonApp.AddPersonView = Ember.View.extend({
username: null,
addPerson: function(event) {
var username = event.context.username;
if (username) {
PersonApp.personController.addPerson(username);
this.set('username', ''); //this does not currently work
}
}
});
The only other issue I'm having is that I don't have access to username the usual way. ie - this.get('username') but in addition I can't clear the textbox value (even though it's shown above).
I'm looking to build a modern version of this gist (previous version of ember) https://gist.github.com/1477225
I see three issues here (perhaps there are more). First, username will not be a field in the event.context, but will actually be the event context. Secondly, I believe you need to specify view.username in the valueBinding, otherwise the controller is the default home of the property (I believe). Then, to set it to initial state you need to set it to null. Third, the target of your action will be the router, so you need to specify the view as the target.
This should work:
{{#view PersonApp.AddPersonView}}
{{view Ember.TextField valueBinding="view.username"}}
{{#with this as username}}
<input type="submit" value="add" {{action addPerson username target="this"}}/>
{{/with}}
{{/view}}
PersonApp.AddPersonView = Ember.View.extend({
username: null
addPerson: function(event) {
var username = event.context;
if (username) {
this.get('controller').addPerson(username);
this.set('username', null);
}
}
});
Also, a better way of creating a new person would be to create a blank person model, bind the controller and view to that, and then save the record, afterwards setting the binding back to null.
You can do the validation and then pass data right now, even with Gidrius' code. The only thing you need to do is write the validation code in the submit handling method. Or, 'cause we`re talking client-side validation anyway, you can do it on field value change or blur, which will give the user almost instant feedback on what he is doing.
I still couldn't get something like this.get('username') to work but I ended up with the following
{{#view PersonApp.AddPersonForm}}
{{view Ember.TextField valueBinding="username"}}
<input type="submit" value="add" {{action addPerson this}}/>
{{/view}}
PersonApp.AddPersonForm = Ember.View.extend({
addPerson: function(event) {
var username = event.context.username;
if (username) {
PersonApp.personController.addPerson(username);
event.context.set('username', '');
}
}
});
probably a bit too late, but might be helpful to someone else.
Usually form field value will be bind to controller or model, so all you need is to have is a submit function in the controller so whenever function will be called you will have access to the fields via bindings.
Here is how it all could look like, assuming you are using latest pre.4 ember
Updated
// DOM part
<form {{action submitForm on="submit"}}>
{{view Ember.TextField valueBinding="username"}}
<button type="submit">add</button>
</form>
And here is a controller
PersonApp.PersonController = Ember.ArrayController({
username: '',
submitForm: function() {
var u = this.get('username'); // saving value to variable
this.set('username',''); // sets username to ''
console.log(u); // will output saved username
}
});