How to get the element reference "this" in EmberJS events? - ember.js

I want to access the target element in the called method in onchange. I have the following code:
Template
<select class="form-control" data-abc="1" onchange={{action 'someMethod' value="target.value"}} >
<option value="">Select</option>
.
.
.
</select>
Component
export default Ember.Component.extend({
actions: {
someMethod(value) {
jQuery(this).data("abc"); // <-- Want to access element here
}
}
});
But, this doesn't work.

When you use value="target.value", someMethod will receive only the value alone and it will not receive default event object.
onchange={{action 'someMethod'}}
your component code would be,
export default Ember.Component.extend({
actions: {
someMethod(event) {
//its event object, you can access event.target to get element or event.target.value to get the value.
}
}
})

I got the answer:
<select class="form-control" data-abc="1" onchange={{action 'someMethod' value="target"}} >
<option value="">Select</option>
.
.
.
</select>
As can be seen in above code, we can pass the target instead of target.value. Then wrap target in jQuery to access desired data attribute.

Related

Accessing model data members in action? Need cast?

This must be a simple concept, but it seems to be assumed rather than covered...
I have a simple model, journal, and component, journal-list:
<select class="form-control" id="journal" onchange={{action 'showJournal' value="target.value"}} >
<option value="" disabled="disabled" selected="selected">Periodicals:</option>
{{#each model as |journal|}}
<option value="{{journal}}"> {{journal.name}}</option>
{{/each}}
</select>
and action:
import Ember from 'ember';
export default Ember.Component.extend({
editstate: Ember.inject.service('edit-state'),
actions: {
showJournal(journal) {
this.get('editstate').selectedJournal = journal;
alert('got ' + journal.name);
this.sendAction('displayJournal', journal);
}
}
});
That selected value is not, as far as I can tell, useful, and the journal instance in the action isn't either. Ember knows it's type Journal (e.g. <em-journalapp#model:journal::ember385:3>), but I can't access the members via, e.g., journal.name, journal.get('name') or anything else I've thought of.
I can pass in journal.id instead, and see it. But if I look up the model from the store, the variable again isn't useful.
Is there some magic to casting or storing model data in variables and getting it back out?
I will encourage you to use ember-power-select addon.
When you say <option value="{{journal}}"> this will be rendered in html like <option value="[object object]"> you can't store object for html attribute.
So you need to include the id like this <option value={{journal.id}}> you can get the selected object through findBy method from options list.
Created this twiddle for this.
application.hbs
<select class="form-control" id="journal" onchange={{action 'showJournal' value="target.value"}} >
<option value="" disabled="disabled" selected={{unless editState.selectedJournal.id 'selected'}}>Periodicals:</option>
{{#each model as |journal|}}
<option value={{journal.id}} selected={{if (eq editState.selectedJournal.id journal.id) 'selected'}}> {{journal.name}}</option>
{{/each}}
</select>
application.js route file,
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return [{id:'1',name:'BBC'},{id:'2',name:'TV'}];
}
});
application.js controller file,
import Ember from 'ember';
export default Ember.Controller.extend({
editState: Ember.inject.service(),
appName: 'Ember Twiddle',
actions: {
showJournal(journalId) {
this.set('editState.selectedJournal',this.get('model').findBy('id',journalId));
console.log('Yes ',this.get('editState.selectedJournal'));
}
}
});
As you maintained state is service so I also did it.
edit-state.js service file
import Ember from 'ember';
export default Ember.Service.extend({
selectedJournal:{},
});

How to dynamically switch disabled=true/false of an input text inside Ember Component?

I have an Ember component that I want to dynamically disable/ enable. Initially I have:
//template.emblem
= input type="text" disabled=true placeholder="Account Number" class="form-control"
When I have disabled=false, it enables the button again. Now I want to make it dynamic. I thought of creating a simple function that returns true or false inside component.js:
//component.js
export default Ember.Component.extend({
...
disableButton(){
return true
},
//template.emblem
= input type="text" disabled=disableButton placeholder="Account Number" class="form-control"
This disables it. However, when I switch disableButton to return false, it is still disabled.
How can I connect the disabled to a function/ property inside component.js?
The template.emblem and component.js folder hierarchy is:
components
|-my-awesome-component
|- template.emblem
|- component.js
Set a property on your component.js and bind it to "disabled" on the input tag in your template. Then you can call the action with your button and set the property to true or false using this.set().
//component.js
import Ember from 'ember';
export default Ember.Component.extend({
disableButton: false,
actions: {
disableButton(){
this.set('disableButton', true);
}
}
});
//template.hbs
<button {{action 'disableButton'}}>Click me</button>
{{input type="text" placeholder="Account Number" class="form-control" disabled=disableButton}}

Load data from database on select box onchange event

Can anyone guide me how to load data from a database when I change the value in the select box? I tried the following code, but when I try to get the "value" and log it says "undefined."
My Component.js
actions:{
filterStudent(){
let filterInputValue = this.get('value');
console.log(filterInputValue);
let filterAction = this.get('filter');
console.log(filterAction);
}
}
My Controller
actions:
{
filterStudentResults(param)
{
if (param !== '')
{
return this.get('store').query('Students', { studentName: param });
}
else
{
return this.get('store').findAll('Students');
}
}
}
My Component.hbs
<select name="newType" onchange={{action "filterStudent" value="target.value"}} class="form-control">
<option value="" disabled selected>Please Select</option>
{{#each model.Students as |newStudents|}}
<option value="{{newStudents.studentId}}">{{newStudents.studentName}}</option>
{{/each}}
</select>
Am calling the component in the Specific template as
{{bind-dropdown model=model Filter=filterStudentResults}}
Am a newbie to EmberJS and appreciate any help. Thanks in Advance :)
In My-Component.js, does not having value as property, You mean to get it from onchange={{action "filterStudent" value="target.value"}} then your action should receive param,
actions:{
filterStudent(selectedValue){
console.log(selectedValue);
}
}
One more problem, I found upon sending action filterStudentResults to component.
The below one is wrong.
{{bind-dropdown model=model Filter=filterStudentResults}}
As you have defined filterStudentResults in controller, you can create closure action and send it to component, so it should be like,
{{bind-dropdown model=model filter=(action 'filterStudentResults')}}
And it should be invoked with selectedValue from component action,
actions:{
filterStudent(selectedValue){
this.sendAction('filter',selectedValue);//this will call controller method with param
}
}
First you need to check the model is ready and you can do so inside route's afterModel(model) hook.
<select name="newType" onchange={{action "filterStudent" value="target.value"}} class="form-control">
Should be re-write to,
<select name="newType" onchange={{action "filterStudent"}} value={{target.value}} class="form-control">
My controller is not getting invoked from the component the sendAction method is not working.
MyController.js
actions:
{
filterStudentResults(param)
{
if (param !== '')
{
return this.get('store').query('Students', { studentName: param });
}
else
{
return this.get('store').findAll('Students');
}
}
}
am calling the component from the template as follows
{{bind-dropdown model=model filter=(action 'filterStudentResults')}}
My Component js
actions:{
filterStudent(selectedValue){
this.sendAction('filterPartsResults',selectedValue)
}
My Componet file name is bind-dropdown and my contollers name is studentsController. The template name is list-studentDetails.

Ember: set attribute's value with action passed from a <select> element

I'm trying to figure out how to set a new Ember Data Record's attribute's value based on choices in a select element, using the {{action}} helper. Here's my situation, simplified:
item.js route. Creates a new record that is available in the template.
model() {
return this.store.createRecord('item');
}
new-item.hbs template. The select-element triggers the action to set the new item's color attribute.
<select {{action 'setColor' model value="target.value" on="change"}}>
<option value="">choose</option>
<option value="white">white</option>
<option value="blue">blue</option>
<option value="red">red</option>
<option value="yellow">yellow</option>
</select>
item.js controller.
actions: {
setColor(item, option) {
console.log(option);
record.set('color', option);
console.log(item);
}
}
Console.log(option) returns nothing. The record.set works fine, I can hardcode the option and the console.log(item) will show that the new item object has the color attribute set.
On the other hand, if I do the action call with just
onChange={{action 'setColor' value="target.value"}}
the color gets logged correctly from the action. But if I add "model" into that action call as before with the {{action ... on="change"}} syntax, console.log(option) returns undefined and console.log(item) returns an Event with no model object attached.
Question is: How do I pass both the target.value and the model to the setColor action?
setColor function will receive event attribute as last argument by default.
onChange={{action 'setColor' value="target.value"}}
so value attribute will be retrieved from event. so this will work as expected. but
onChange={{action 'setColor' model value="target.value"}}
value attribute will try to get target.value from model property as we sent model as first argument, and default event argument will be passed as second argument. so combining won't work.
Solution would be
onChange={{action 'setColor' model}}
and inside actions,
actions: {
setColor(model, event) {
let value = event.target.value;
}
}
I just had the same issue, but was not satisfied with the accepted answer here, since I had 2 different inputs (dropdown & text-field) that used the same action (but not same path to resolve the value), so I had to use value="..." as part of the action helper.
I found the answer in https://stackoverflow.com/a/45762071/45948
so in your case it would be:
onChange={{action (action 'setColor' model) value="target.value"}}
and then simply:
actions: {
setColor(model, option) {
...
}
}

How to clear the typeahead input after a result is selected?

I'm using the ng-bootstrap typeahead component to search a customer database. When the user selects a customer from the typeahead results list, I navigate to a customer details page. I've got this working, but I want to clear the input field after navigation has taken place. I've tried setting the model to null or an empty string in the selectItem event logic, but this isn't working:
customer-search-typeahead.component.html
<template #resultTemplate let-r="result" let-t="term">
<div>
<div>
{{r.resource.name[0].given}} {{r.resource.name[0].family}}
</div>
<div>
{{r.resource.birthDate | date: 'dd/MM/yyyy'}}
</div>
</div>
</template>
<input type="text" class="form-control" [resultTemplate]="resultTemplate" (selectItem)="onSelect($event)"
[(ngModel)]="model" placeholder="Start typing a customer name..." [ngbTypeahead]="search"/>
customer-search-typeahead.component.ts
#Component({
selector: 'customer-search-typeahead',
template: require('./customer-search-typeahead.component.html'),
styles: [`.form-control { width: 300px; }`]
})
export class CustomerSearchTypeaheadComponent {
model: any;
searching: boolean;
constructor(private customerService: CustomerService, private router: Router) {}
onSelect($event) {
this.router.navigate(['/customers', $event.item.resource.id]);
this.model = null;
};
search = (text$: Observable<string>) =>
//omitted for brevity
}
The typeahead input looks like this after a selection has been made:
Solution
customer-search-typeahead.component.html
<input type="text" class="form-control" #input [ngbTypeahead]="search" (selectItem)="onSelect($event); input.value='' ">
customer-search-typeahead.component.ts
onSelect($event, input) {
$event.preventDefault();
this.router.navigate(['/customers', $event.item.resource.id]);
};
The issue you witnessing arises from the fact that the NgModel directive is updating model binding asynchronously and the actual model is updated after the onSelect method gets executed. So your model update gets overridden by the NgModel functionality.
Fortunately we (ng-bootstrap authors) got all the flex points in place to cover your use-case :-) There are a couple of things that you could do.
Firstly the $event object passed to the onSelect method has the preventDefault() method and you can call it to veto item selection (and as a result writing back to the model and input field update).
$event.preventDefault() will make sure that the model is not updated and the input field is not updated with the selected item. But text entered by a user will still be part of the input so if you want to clear up this as well you can directly update the input's value property.
Here is code demonstrating all those techniques together:
onSelect($event, input) {
$event.preventDefault();
this.selected.push($event.item);
input.value = '';
}
where input argument is a reference to the input DOM element:
<input type="text" class="form-control" #input
[ngbTypeahead]="search" (selectItem)="onSelect($event, input)">
Finally here is a plunker showing all this in practice: http://plnkr.co/edit/kD5AmZyYEhJO0QQISgbM?p=preview
The above one is template ref value solution.
This is for ngModel solution.
Html code:
<input type="text" class="form-control" [resultTemplate]="resultTemplate" (selectItem)="onSelect($event)"
[(ngModel)]="model" placeholder="Start typing a customer name..." [ngbTypeahead]="search"/>
Component code:
onSelect($event) {
$event.preventDefault();
this.model = null;
this.router.navigate(['/customers', $event.item.resource.id]);
};
$event.preventDefault();
for ngModel value change empty