So I have an API that generates forms from a database. The API for the fields returns:
{
"formFields": [
{
"module": 1,
"fieldName": "Global Title",
"fieldPosition": "1",
"fieldType": "select",
"fieldEditable": true,
"dataTable": "message",
"dataKey": "calling_gt",
"dataValue": "id",
"id": 1,
"createdAt": "2015-10-15T13:59:30.764Z",
"updatedAt": "2015-10-15T13:59:30.764Z"
}
]
}
I have multiple Ember components. Essentially an Add Parameter component that loops through the fields related to the component. Below is loading the select component passing in the Model from the database table and the fields to use for the Key->Value:
{{#each model.fields as |field|}}
{{#if (eq field.fieldType "select")}}
{{form/select-field model=field.dataTable label=field.fieldName key=field.dataKey value=field.dataValue selected=1}}
{{/if}}
{{/each}}
The select-field component then generates a select that brings out the Model data from the values provided in the Add Parameter component, like so:
<div class="form-group">
<label for="{{key}}">{{label}}</label>
{{#x-select value=optionValue action="selectOption" class="form-control" id=key}}
{{#each componentModel as |option|}}
{{#x-option value=option.calling_gt}}{{option.calling_gt}}{{/x-option}}
{{/each}}
{{/x-select}}
</div>
But as you can see, in x-option, I'm currently hard-coding the values, where they should be using the key=field.dataKey. But as I'm looping through and my componentModel -> Options don't hold that variable, I'm not sure how to pass it into the x-option value.
My component.js for select-field looks like this, if it helps:
import Ember from 'ember';
export default Ember.Component.extend({
componentModel: function() {
return this.store.findAll(this.get('model'));
}.property(),
actions: {
selectOption(value, component) {
Ember.Logger.debug("Option " + component + " with value " + value + " selected");
this.set('optionValue', value);
}
},
});
Does anyone know how to bring out the data using the key and value passed into the select-field component?
Sounds like you might be able to use the get helper. http://emberjs.com/api/classes/Ember.Templates.helpers.html#method_get
{{#x-option value=(get option key)}}
{{get option key}}
{{/x-option}}
Related
I want to have a search field inside a component that can be placed anywhere in the app. It can appear on any template, or nested in components. The search form would accept user input (search term) and submit would trigger a search action which transitions to a results template.
Seems simple enough, but I can't figure out how to make an action globally available. And if I could, how do you pass the inputted term to the action in the first place? There's surprisingly little info on how to handle form submits with Ember CLI.
Thus far I've just been submitting a regular form with action='/results'. But that's obviously reloading the app.
I've been messing with creating an action in the index controller like this:
export default Ember.Controller.extend(defaultParams, {
term: '',
actions: {
keywordSearch() {
this.transitionToRoute('results', { queryParams: { q: this.get('term') }});
}
}
});
Then passing a closure action down to my search component, which is nested 2 deep from the index template.
index.hbs:
{{index-search keywordSearch=(action "keywordSearch")}}
index-search.hbs (component):
{{search-field keywordSearch=keywordSearch }}
search-field.hbs (nested component):
<form {{ action (action keywordSearch) on='submit' }}>
{{ input value=term }}
<button type="submit">Search</button>
</form>
And that will run the action, but the term is not supplied. How do you supply term to the closure action?
And...do I really need to pass the action down to every single place the search field is going to appear in the app, or is there an easier way to do it?
Instead of writing actions in all components and routes, you can create a service for search. Inject the service into the component and handle the route transition from service method. Check the sample code below,
Search-component.hbs
<form {{ action (action search) on='submit' }}>
{{ input value=keyword }}
<button type="submit">Search</button>
</form>
Search-component.js
export default Ember.Component.extend({
globalSearch: Ember.inject.service('search'),
actions: {
search() {
const { keyword } = this.getProperties('keyword');
this.get('globalSearch').showResults(keyword).then(() => {
alert('Success');
}, (err) => {
alert('Error while searching: ' + err.responseText);
});
}
}
});
Service - app/services/search.js
import Ember from 'ember';
export default Ember.Service.extend({
init() {
this._super(...arguments);
},
showResults(keyword) {
// write code for transition to search results route here
}
});
I can't seem to understand how to do the following looking over the docs for "ember power select".
I have a model user:
export default Model.extend({
"state": attr('string')
});
Stored as a value in the DB is: NY for state
I also have the following data to load into the ember power select options:
stateList: [
{
label: 'New Jersey',
value: 'NJ'
},
{
label: 'New York',
value: 'NY'
},
]
The following handlebar code will load in the states and display them. You can search and select the state:
{{#power-select
options=stateList
searchField="label"
selected=state
onchange=(action (mut state))
as |state|
}}
{{state.label}}
{{/power-select}}
The issue... on select of 'New York', I would like the stored value of 'state' to be 'NY'
Right now it's simply storing the entire object. I understand through 'onchange' I can set the value, but I don't really understand how you set the value to 'NY' and have it selected?
I've tried doing
this.set('state',selection.value)
But I think it's looking for the index of the object, I however simply want to pass 'NY' and not a whole object... is this possible?
Answer to your question is NO(AFAIK). But you can achieve this using computed property.
Component js code
import Ember from 'ember';
export default Ember.Component.extend({
stateList: [{'label': 'New Jersey','value': 'NJ'},{ 'label': 'New York','value': 'NY'}],
state:'NY',
selectedState: Ember.computed('state', function(){
return this.get('stateList').findBy('value',this.get('state'));
}),
actions:{
setSelectedState(selectedVal){
this.set('state',selectedVal.value);
}
}
});
Component hbs code
{{#power-select
options=stateList
searchField="label"
selected=selectedState
onchange=(action 'setSelectedState')
as |state|
}}
{{state.label}}
{{/power-select}}
{{yield}}
Here is Ember-Twiddle
I have created the dropdown of country name and set country code as a value for each option .I want to set the default country name and also listen the selection change of the dropdown .I have tried the following code
HTML
<select class="left-float" onchange={{action selectionchange}} >
{{#each model.countries as |country|}}
<option value="{{country.code}}" selected={{eq country.code "IN"}} >{{country.name}}</option>
{{/each}}
</select>
Router
App.SignupRoute = Ember.Route.extend({
model: function () {
return Ember.RSVP.hash({
countries: Countries
});
},
actions:{
selectionchange:function(value){
}
}
});
Could please help me to resolve this
#Sindhu,
Another simple solution would be to create a custom helper eq. The custom helper would look like:
import Ember from 'ember';
export function eq(params) {
return params[0] === params[1];
}
export default Ember.Helper.helper(eq);
Working example: https://ember-twiddle.com/d4aaf5790e40b20ce492. You can change the emberjs version by clicking Dependencies -> Ember.js -> version. The code works with v2.0.2.
Let me know if this solution works for you.
Here is how you can implement the selectionChange:
HBS:
<select class="left-float" onchange={{action "selectionchange" value="target.value"}} >
{{#each countries as |country|}}
<option value="{{country.code}}" selected={{eq selectedCodecountry.code}}>
{{country.name}}
</option>
{{/each}}
</select>
Inside controller
countries: [
{code:"IN", name: "India"},
{code: "US", name: "USA"},
{code: "UK", name: "UK"}
],
selectedCode: "US",
actions: {
selectionchange(code) {
this.set('selectedCode', code);
}
Hope it helps.
Here's the situation:
I wrote a component facility-search which searches through some fixture data. I put multiple instances of {{facility-search}} on same template (Tab Pages). This component has some input boxes where we can write search keywords. I want to observe change in value of input box and update the same to another instance of component so that both of them will be in sync.
This is what I'm doing in components/facility-search.js
import Ember from 'ember';
import Em from 'ember';
var FacilitySearchComponent = Ember.Component.extend({
// Tag name appears in HTML
tagName: 'div',
// RandomNumber
searchBoxId: null,
// All Facilities
allFacilities: null,
// Search Values
textFacility: "",
textCountry: "",
textSpecies: "",
// Text Input Ids needed for <label for="id"/>
// Elements in multuple components should not have same id
textFacilityId: 'text-facility',
textCountryId: 'text-country',
textSpeciesId: 'text-species',
// Initialize Ids
randomNumber: function(){
this.set('searchBoxId',(Math.floor(Math.random() * 100) + 1));
this.set('textFacilityId', this.get('textFacilityId') + "-" + this.get('searchBoxId'));
this.set('textCountryId', this.get('textCountryId') + "-" + this.get('searchBoxId'));
this.set('textSpeciesId', this.get('textSpeciesId') + "-" + this.get('searchBoxId'));
}.on('init'),
// When component is inserted
didInsertElement: function() {
this.set('filteredFacilities', this.get('allFacilities'));
},
// Observe Search Values
watchForFilterChanges: function() {
this.filterResults();
}.observes('textFacility', 'textCountry', 'textSpecies'),
// Filter Data
filterResults: function() {
var facilities = // Some mechanism to filter data
self.sendAction('updateFacilities', facilities);
}.on('allFacilities'),
actions: {
clearSearch: function() {
this.set('textFacility', null);
this.set('textCountry', null);
this.set('textSpecies', null);
this.filterResults();
},
}
});
export default FacilitySearchComponent;
This is my templates/components/facility-search.hbs
<div class="card">
<div class="card-content directory-search">
<div class="card-title grey-text text-darken-3">
<h4>Search Facilities</h4>
<h4><small class="teal-text">{{filteredFacilities.length}} total</small></h4>
</div>
<form {{action "textSearch" this on="submit" data="lol"}}>
<div class="row">
<div class="input-field col s12">
<label for="{{textFacilityId}}">Search by facility</label>
{{input value=textFacility type="text" id=textFacilityId label="Facility Name"}}
</div>
<div class="input-field col s12">
<label for="{{textCountryId}}">Search by country</label>
{{input value=textCountry type="text" id=textCountryId label="Facility Country"}}
</div>
<div class="input-field col s12">
<label for="{{textSpeciesId}}">Search by species</label>
{{input value=textSpecies type="text" id=textSpeciesId label="Facility Species"}}
</div>
</div>
</form>
<a {{action 'clearSearch'}} class="waves-effect waves-light btn"><i class="material-icons right">clear_all</i>clear search</a>
</div>
</div>
This is my controllers/map.js
import Ember from 'ember';
import pagedArray from 'ember-cli-pagination/computed/paged-array';
export default Ember.Controller.extend({
// Facility to be shown in modal
selectedFacility: null,
// Facilities to be shown in search
filteredFacilities: [],
// Initialize filteredFacilities to model
initializeFacilities: function() {
this.set('filteredFacilities', this.get("model"));
}.observes('model'),
actions: {
showFacilityInModal: function(facility){
this.set('selectedFacility', facility);
},
updateFacilities: function(facilities){
this.set('filteredFacilities', facilities);
},
}
});
This is routes/map.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('facility');
},
});
And this is how I'm using component in templates/map.hbs
{{facility-search allFacilities=model updateFacilities='updateFacilities'}}
I learned that if we put component multiple times; it will have complete new instances. So updating variables textFacility and others cannot be observed in another instance of same component. But I want to update those values in another instance as well. Any idea how we can sync the states of multiple instances of same component?
If I understand you question you want to share values between all component so if you change it in one, it changes in the other.
You can do this:
text : {
facility: "",
country: "",
species: "",
}
instead of
textFacility: "",
textCountry: "",
textSpecies: "",
Declaring it as an object means it will be shares across all component instances like a static variable.
I found a workaround !! Not sure if its a right way of doing it.
I put variables textCountry in controllers/map.js and passed it to component as follows:
{{facility-search textCountryComponentSpec=textCountry allFacilities=model updateFacilities='updateFacilities'}}
where textCountryComponentSpec holds that value in component. Then I observed changes in textCountryComponentSpec in component and update textCountry in controller. Since it is passed to components it reflects the changes.
If you know a better way please post.
Can I and should i pass arrays to components in ember?
For example if I wanted to template something that renders a couple of radiobuttons with labels:
<label for="media">Media</label><input type="radio" name="entry.1602323871" value="media" id="media" />
<label for="guest">Guest</label><input type="radio" name="entry.1602323871" value="guest" id="guest" />
Could I somehow pass an array with this content and loop through it.
Media, media
Guest, guest
Yeah, You can pass anything to components. Just a try to the radio-buttons
//Basic radio Component
App.RadioButton = Ember.Component.extend({
tagName : "input",
type : "radio",
attributeBindings : [ "name", "type", "value", "checked:checked" ],
click : function() {
this.set("selection", this.$().val());
},
checked : function() {
return this.get("value") === this.get("selection");
}.property('selection')
});
Em.Handlebars.helper('radio-button',App.RadioButton);
Updated (component name should be dasherized)
Working Radio Demo
It's now a tiny bit easier to get radio-buttons into your project (if you're using ember-cli) by using the ember-radio-buttons addon
Install:
npm install ember-radio-buttons --save-dev
Usage:
{{radio-button value='one' checked=selectedNumber}}
{{radio-button value='two' checked=selectedNumber}}
Upped #selva-G's answer.
I found that using the ButtonGroup Component from Bootstrap-for-Ember is actually cleaner.
Here's what I've done:
In my view's template:
<script type="text/x-handlebars" data-template-name="myview">
{{bs-btn-group contentBinding="things" selectedBinding="selectedThing"}}
</script>
In that view's controller (which doesn't necessarily need to be an ArrayController, rather can be a generic Ember Controller):
App.MyviewController = Ember.ArrayController.extend({
things: [
Ember.Object.create({value: 'first', title: 'First Thing'}),
Ember.Object.create({value: 'second', title: 'Second Thing'}),
Ember.Object.create({value: 'third', title: 'Third Thing'})
],
selectedThing: 'second'
selection: function() {
console.log(this.get('selectedThing');
}.observes('selectedThing')
});