Set focus on a text box in ember js - ember.js

I am trying to change a text to a text box. If a user clicks the text, it will change to a test box and so they can edit the content. I have done that. I am able to set the focus by below code.
{{input value=animal.name autofocus=true}}
It only works for the first time. If I focus-out, the text box will again change to text content. If I click the text again, I am able to see the text box, but it is not in focus. Below is the code I am trying.
Component file:
import Ember from 'ember';
export default Ember.Component.extend({
isTextBox: false,
actions: {
editTest() {
this.set('isTextBox', true);
}
},
focusOut() {
this.set('isTextBox', false);
},
});
Template file:
{{yield}}
<center><h2>
<div onclick = {{action 'editTest'}}>
{{#if isTextBox}}
{{input value=animal.name autofocus=true}}
{{else}}
{{animal.name}}
{{/if}}
</div>
</h2></center>
I am new to ember and I trying to do the focus without using jQuery.
Here is the twiddle https://ember-twiddle.com/d832c6540ba94901a6c42d5bb3cfa65e?openFiles=templates.components.text-input.hbs%2Ctemplates.components.text-input.hbs.

You can do this in Plain Old Javascript as follows:
didRender(){
// This works but is not very ember-ish
// document.getElementById('cat-text').focus();
// For a component, you can do this
this.get('element').querySelector("#cat-text").focus();
}
This assumes you have an input like this:
{{input id='cat-text' value=animal.name }}
Ember 2.13+ is no longer dependent on jQuery (although some of the add-ons that you use may be), so you have the possibility of eliminating 35kb (min + gzip) that jQuery would add to the payload of your app.

To expand on kumkanillam's answer you can just set the focus at the end of the render lifecycle if it's rendering a textbox. Since you've only got one text input in your component you can just find it with a selector. No need to worry about using jQuery here that's what it's there for so it is the Ember way.
didRender(){
if(this.get('isTextBox')){
this.$('input[type=text]:first').focus();
}
}

You can provide id to input helper,
{{input id='cat-text' value=animal.name }}
You can make it focus text field for every rendering,
didRender(){
this.$('#cat-text').focus();
}

components/input-text-focused/component.js:
import TextField from '#ember/component/text-field';
export default TextField.extend({
didInsertElement(...args) {
this._super(...args);
const element = this.get('element');
if (element) element.focus();
},
});
{{input-text-focused type="text" name="search" value=search}}

Related

Ember : Close/Cancel a form using 'Esacpe' Key

I have form and and I want to close my form with the escape key. I can't figure out how to implement it. I know I need some keyDown event or so! but where to implement and how do I make it trigger?
<form id = 'myform' {{action 'save' on='submit'}}>
<div class="usersgroups-headline-label admin-form-headline-label">
{{#if page.isEdit}}Edit{{else}}Add{{/if}}{{#if page.holder.isUser}} User{{#if page.isEdit}} : {{page.oldHolder.Id}}{{#if showHolderName}} ({{page.oldHolder.Name}}){{/if}}{{/if}}{{/if}}{{#if page.holder.isGroup}} Group{{#if page.isEdit}} : {{page.oldHolder.Id}}{{/if}}{{/if}}
</div>
.
.
.
.
<div class="action-buttons">
{{#if page.isEdit}}
{{form-button value='Save' enabled=page.canUpdate type='submit'}}
{{else}}
{{form-button value='Add' enabled=page.canSave type='submit'}}
{{/if}}
{{form-button value='Cancel' action='cancel'}}
</div>
I suggest using an addon such as ember-keyboard which abstracts the behavior you describe. I can also recommend to take a look at ember-modal-dialog which, while not what you may look for at first glance provides similar behavior and also explains a lot on how to implement keyboard events in their own README.
If you want to handle this by hand, you should wrap your form in a component that handles the keyboard event for you, if the form is not in a component already.
Inside the form you will have to use the didInsertElement method to start listening for the event and the willDestroyElement method to stop listening when the component gets teared down.
In current Ember (~2.18) this looks something like this:
import Component from '#ember/component';
export default Component.create({
onEscapeKey: () => {},
didInsertElement() {
this._escapeKeyCallback = (event) => {
if (event.key === 'Escape') {
this.onEscapeKey();
}
};
window.addEventListener('keypress', this._escapeKeyCallback);
},
willDestroyElement() {
window.removeEventListener('keypress', this._escapeKeyCallback);
},
});
You can then use this component inside your template and use the onEscapeKey property to trigger an action when the escape key is pressed.
{{escape-key-component onEscapeKey=(action 'doSomething')}}

how to trigger a function automatically in ember.js action helper?

I am new to ember.js and working on an application to highlight text like below:
<div>
<h3 class = "title" {{action "highlight" "title"}}>{{document.title}}</h3>
<div class = "date" {{action "highlight" "date"}}>{{document.date}}</div>
<p class = "content" {{action "highlight" "content"}}>{{document.contents}}
</p>
</div>
I created a function highlight which will get the class name and highlight the search text the user input. This function works fine, but I have to click to trigger this highlight function. Is there any way in ember action helper can trigger this function whenever the div node rendered or automatically triggered?
What you probably need is to implement the hook didInsertElement, this will trigger as soon your component got rendered in the screen.
Example:
import Component from '#ember/component';
export default Component.extend({
didInsertElement() {
this._super(...arguments);
const title = this.element.querySelector('.title');
// do something with the title
},
});
More information about didInsertElement can be found in this guide: https://guides.emberjs.com/v2.18.0/components/the-component-lifecycle/
I don't understand your use-case entirely, but I think actions are not a good choice for your problem. I would recommend reading the ember guides about computed properties. These properties are recomputed everytime an underlying property changes.
highlightedContent: computed('userInput', 'content', function() {
....
//return computedContent;
})
I would also recommend reading the guides about handlebar-helpers. You could write a highlight-helper.

Ember integration test fails after click event

I have this integration test:
test('can change chord text', function(assert) {
this.render(hbs`{{chart-editor-chord chord=chord}}`);
this.$().click();
assert.ok(!!this.$('.chord-input').length);
});
but the assertion fails, the component template looks like this:
<div {{action 'changeChord'}} class="measure-chord chord-big">
{{#if chord.editing}}
<input type="text" value="{{chord.name}}" class="chord-input">
{{else}}
{{chord.name}}
{{/if}}
</div>
and the component code:
import Ember from 'ember';
export default Ember.Component.extend({
store: Ember.inject.service(),
actions: {
changeChord() {
this.chord.set('editing', true);
}
}
});
I'm updating the chord model in the changeChord() action and it does work if I test in the browser, but the integration test fails. So, does this change in the model have to be rendered synchronously to the template? I tried using wait() in the test but that doesn't make a difference. So how should I test this?
While I'm trying to create a twiddle for you, I found three things:
Where do you create chord mock in your test?
You are not sending event to the correct html component. Use this.$('.measure-chord') or this.$('.chord-big').
Instead of this.chord.set you should use this.get('chord').set. Actually Ember.set(this, 'chord.isEditing', ...) is even better.
And bonus: You don't need a div wrapper, component does this for you.
twiddles:
working copy
without div
It looks like your click helper is clicking the div that your component.js controls instead of the initial div in your template. If you specify the div in your click helper it should work:
this.$('.measure-chord').click();

Prevent click into {{input}} component from propagating/bubbling up (ember)

I have an {{input}} inside of an element that itself has an action.
How do I prevent clicking into the input from triggering the click event on the parent element?
I have tried bubbles=false.
I have also tried extending {{input}} and in the extended input I caught the click event and called event.preventDefault()
Please try my test case: https://ember-twiddle.com/a2cee9abd63a7400124e2745a7820cf8?numColumns=2&openFiles=controllers.application.js%2Ctemplates.application.hbs
Example hbs:
<div {{action "alert"}}>
I don't want the click into the input to trigger the div's onlick. Help!<br/>
{{input bubbles=false}}
</div>
Define component say my-input which extends Ember.TextField and define click function and return false to prevent bubbling.
import Ember from 'ember';
export default Ember.TextField.extend({
click(){
console.log('click my input');
return false;
}
});
For my-input component, you dont need to define anything to handle click event while including the component. you can just say {{my-input }}
Here is the working twiddle

Integrating ember-modal-dialog inside a site header Ember 2.0

I'm very new to the Ember framework and I have a question regarding getting a modal setup. I have the site-header as a component. When I click a login button I'd like for a modal to popup. I found the Ember Modal Dialog
plugin and was able to set it up so that there is a modal always shown in application.hbs. However, I'm having trouble understanding a couple of things but first here are my files.
application.hbs
{{site-header}}
{{outlet}}
{{#if isShowingModal}}
{{#modal-dialog close="toggleModal"
alignment="center"
translucentOverlay=true}}
Oh hai there!
{{/modal-dialog}}
{{/if}}
{{site-footer}}
site-header.js
import Ember from 'ember';
export default Ember.Component.extend({
isShowingModal: false,
actions: {
toggleModal: function() {
this.toggleProperty('isShowingModal');
}
}
});
So, I have this code for the button in my site-header.hbs:
<li class="login-button"><button class="btn btn-block btn-danger" {{action 'toggleModal'}}>Login</button></li>
As I understand it, action is saying to find toggleModal property in the site-header.js and execute the function above which does the property toggling.
However, how does application.hbs "see" the value of isShowingModal? Can it even see that value since the modal isn't showing up?
When most developers have modals, do they all go inside application.hbs since you want them to appear in the middle of the screen {{outlet}} area? How can you improve the process for including multiple modals?
What changes should I make to make the modal show up when the user clicks a button in the header?
Ok give this a try. In site-header.js
export default Ember.Component.extend({
actions: {
toggleModal() {
this.sendAction('toggleModal');
}
}
});
Application.hbs
{{site-header toggleModal='toggleModal'}}
{{outlet}}
{{#if isShowingModal}}
{{#modal-dialog close="toggleModal"
alignment="center"
translucentOverlay=true}}
Oh hai there!
{{/modal-dialog}}
{{/if}}
{{site-footer}}
The application controller
export default Ember.Controller.extend({
actions: {
toggleModal() {
this.toggleProperty('isShowingModal')
}
}
})
So the action is sent from the site-header component to the site-header component. From there it gets sent to the application controller. In application.hbs, toggleProperty='toggleProperty' connects the action from the component to the controller's action hash. In the controller action hash, it gets handled by toggleModal() and toggles the isShowingModal property. When the modal is closed, ember-modal-dialog fires an close action that is handled by the toggleModal() which again toggles the isShowingModal property.
Application template cannot see the properties of site-header. Only site-header component can see its properties. There are other methods to access them.
It is not necessary to include all modals in application. The plugin will most probably handle the positioning. In your case, you can move the modal code to the site-header component also.
Put the modal code in your site-header template. (or) You can have isShowingModal variable in application, send an action from site-header to application and toggle its value.