Seems it's a bad practice to retrieve data from your component, but this is kind of an experiment (hope the code explains itself)
import Ember from 'ember';
export default Ember.Component.extend({
store: Ember.inject.service(),
items: [],
init () {
this._super(...arguments);
var store = this.get('store');
let items = store.findAll('dealtype');
}
});
While I can see my api is being hit (debug) and data returned (ember inspector) when looping over "items" in my component template, it's always empty.
Curious what I'm doing wrong (learning still)
You didn't set items in your init
Set that like :
...
let items = store.findAll('dealtype');
this.set('items', items);
Related
I've got a filter that works once, with whatever values are pre-initialized in, just at initial initiazation, but never updates. editstate is a service; in this case it just provides those exposed variables. So the idea is that when editstate.filterValue and editstate.filterField are changed, the filter should update.
But it doesn't.
I've tried having them be local computed values also, but no dice. The only programmatic thing that works so far is to unrender and rerender the entire component using a handlebars {{#if toggle.
export default Ember.Component.extend({
store: Ember.inject.service(),
editstate: Ember.inject.service('edit-state'),
filteredList: Ember.computed.filter('model', function(current, index, all) {
return current.get(Ember.get(this.get('editstate'), 'filterField')) == Ember.get(this.get('editstate'), 'filterValue');
}),
What am I missing? I don't see any API for forcing a filter to re-compute, nor to tell it more explicitly to watch these values.
Update: I found a terrible hacky way to accomplish my goal: In my service, I create but don't save a record in the model that I'm filtering, just to force updates. Whenever the filter parameters change, I update that record with the current time (millis). And of course it's always filtered out by the filter.
It's ugly, it's probably evil... is there a better way?
Define every variable (that you use in computed function) at computed definition:
export default Ember.Component.extend({
store: Ember.inject.service(),
editstate: Ember.inject.service('edit-state'),
filteredList: Ember.computed('model', 'editstate.filterField', 'editstate.filterValue', function() {
return this.get('model').filter((current) => {
return current.get(Ember.get(this.get('editstate'), 'filterField')) === Ember.get(this.get('editstate'), 'filterValue');
}
}),
...
or even more readable:
export default Ember.Component.extend({
store: Ember.inject.service(),
editstate: Ember.inject.service('edit-state'),
filteredList: Ember.computed('model', 'editstate.filterField', 'editstate.filterValue', function() {
let filterField = Ember.get(this, 'editstate.filterField');
let filterValue = Ember.get(this, 'editstate.filterValue');
return this.get('model').filterBy(filterField, filterValue);
}),
...
I have a model in a route that has data i'd like to chart using a chart plugin (ember-google-charts or ember-charts specifics do not matter to my issue)
in routes/server.js:
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return Ember.RSVP.hash({
stats: this.store.query('stat', {filter: {serverId: params.server_id}})
server: this.store.findRecord('server', params.server_id),
})
},
setupController(controller, models) {
controller.setProperties(models);
}
}
});
My issue where/how to make stats into a proper array (using timechecked and players attributes) in order to pass the data into a charting plugin in the template.
I've tried something similar to:
stats: this.store.query('stat', {filter: {serverId: params.server_id}}).then(items => {
//modify items here
})
but i have a hard time figuring out how to manipulate the data and build a new array without throwing errors using a forEach (which exists on the ember Array class)
1.You can use forEach to iterate and return result you created. refer this answer for work with ember model
stats: this.store.query('stat', { filter: { serverId: params.server_id } }).then(items => {
//modify items here
let result =[];
items.forEach(function(element) {
let value1 = element.get('propertyName');
result.pushObject(value1);
}, this);
return result;
})
2. If you just want plain array then you can use toArray method.
I have component that I want to provide data too. I am using Ember-CLI if that helps.
The component is a map that I am loading onto the page that I than want to place markers on. I used a component so I could use the didInsertElement method to get access to the element once it is ready.
export default Ember.Component.extend({
componentMap: '',
didInsertElement: function() {
navigator.geolocation.getCurrentPosition(position => {
//Initialize map...
this.populateMap();
});
},
populateMap: function() {
//Get store
var store = this.get('parentView.targetObject.store');
console.log(store);
//Search Store
var data = store.find('restaurant');
//Where is the data?!
data.map(item => {
console.log(item.get('name'));
});
}
});
I am having an issues getting the data from a store. I have seen a couple methods, here shows two different methods. First being the this.get('parentView.targetObject.store') or this.get('targetObject.store'). I have also tried the {{component store=store}} method, but that was not working for me either. This might have to do with a fundamental lack of understanding of data flow in an ember app.
I am using Ember CLI and I am wondering if it has anything to do with the context of this inside modules?
If I am way off base as to how I should do this, please let em know!
UPDATE: Adding route, controller and template for context.
Route
export default Ember.Route.extend({
model: function() {
return this.store.find('restaurant');
}
});
Controller
export default Ember.Controller.extend({
actions: {
add: function() {
var $addForm = $('.add-form');
$addForm.show();
}
}
});
Template (index.hbs, which is output in application.hbs {{outlet}})
{{main-map store=store}}
Thanks.
What is happening is as follows:
The model associated with your control is populated as an array of restaurants, not a single map or anything of that sort.
return this.store.find('restaurant'); returns an array of restaurants from the store which ultimately populates the model of your controller.
If you want access to the data contained within your model in your component, you should pass the model as an argument into your component.
So, you can pass the array of restaurants as follows (rename the property as appropriate):
{{main-map data=model}}
Or, if in theory you wanted to display a component for each restaurant:
{{#each restaurant in model}}
{{your-component name=restuarant.name}}
{{/each}}
I am creating my first ember app using ember-cli and now facing issue with setting new property at controller.
I have model associations as follows:
Offer has many Items and item belongs to Offer.
Now i want find the offer without items and return that offer-id.
Offers.index controller:
export default Ember.ArrayController.extend({
pendingOffer: function () {
var pending_offer = this.findBy('items.length',0);
var offer_id = pending_offer ? pending_offer.id : null
return offer_id;
}.property('offers.#each.items.length')
});
But here when i add item to an existing offer, it still returns the old offer, it works proper on page-refresh, but property is not updating..
there might be some issue with property('offers.#each.items.length') Can anyone please help me to make it work?
Thanks
Finally able to get it done..
Offer model:
import DS from 'ember-data';
var attr = DS.attr,
hasMany = DS.hasMany;
export default DS.Model.extend({
// other attributes
itemCount: function() {
return this._data.items.length;
}.property('this.items.#each')
});
Offers.index controller
import Ember from 'ember';
export default Ember.ArrayController.extend({
pendingOffer: function () {
var pending_offer = this.findBy('items.length',0);
var offer_id = pending_offer ? pending_offer.id : null;
return offer_id;
}.property("content.#each.itemCount")
});
And its done..!!
Now whenever I add item to an offer it updates pendingOffer value.
i have a component and when user click on component it add some value to store,i try to use this way but i get an error :
OlapApp.MeasureListItemComponent = Ember.Component.extend({
tagName: 'li',
isDisabled: false,
attributeBindings: ['isDisabled:disabled'],
classBindings: ['isDisabled:MeasureListItemDisabled'],
actions: {
add: function(measure) {
var store = this.get('store');
store.push('OlapApp.AxisModel', {
uniqueName: measure.uniqueName,
name: measure.name,
hierarchyUniqueName: measure.hierarchyUniqueName,
type: 'row',
isMeasure: true,
orderId: 1
});
}
}
});
and this is error:
Uncaught TypeError: Cannot call method 'push' of undefined MeasureListItemComponent.js:18
is it posible to push record to store from component? why i cant access to store ?
my model name is 'AxisModel' and application namespace is 'OlapApp'
Since Ember v1.10, the store can be injected to components using initializers, see: http://emberjs.com/blog/2015/02/07/ember-1-10-0-released.html#toc_injected-properties:
export default Ember.Component.extend({
store: Ember.inject.service()
});
In a component the store does not get injected automatically like in route's or controller's when your app starts. This is because components are thought to be more isolated.
What follows below is not considered a best practice. A component should use data passed into it and not know about it's environment. The best way to handle this case would be using sendAction to bubble up what you want to do, and handle the action with the store in the controller itself.
#sly7_7 suggestion is a good one, and if you have a lot of components from where you need access to the store then it might be a good way to do it.
Another approach to get to your store could be to get the store your component surrounding controller has reference to. In this case it doesn't matter which controller this is because every controller has already a reference to the store injected into it. So now to get to your store could be done by getting the component's targetObject which will be the controller surrounding the component and then get the store.
Example:
OlapApp.MeasureListItemComponent = Ember.Component.extend({
...
actions: {
add: function(measure) {
var store = this.get('targetObject.store');
...
}
}
});
See here for a working example.
Hope it helps.
Update in response to your comment having nested components
If for example you child component is only nested one level then you could still refer to parent's targetObject using parentView:
App.ChildCompComponent = Ember.Component.extend({
storeName: '',
didInsertElement: function() {
console.log(this.get('parentView.targetObject.store'));
this.set('storeName', this.get('parentView.targetObject.store'));
}
});
Updated example.
Since Ember 2.1.0
export default Ember.Component.extend({
store: Ember.inject.service('store'),
});
before Ember 2.1.0 - dependency injection way
App.MyComponent = Ember.Component.extend({
store: Ember.computed(function() {
return this.get('container').lookup('store:main');
})
});
before Ember 2.1.0 - controller way
You can pass store as property from controller:
App.MyComponent = Ember.Component.extend({
value: null,
store: null,
tagName: "input",
didInsertElement: function () {
if (!this.get('store')) {
throw 'MyComponent requires store for autocomplete feature. Inject as store=store'
}
}
});
Store is available on each controller. So in parent view you can include component as follows:
{{view App.MyComponent
store=store
class="some-class"
elementId="some-id"
valueBinding="someValue"
}}
Passing properties to component is documented here
The current ember-cli way to do this appears to be with an initializer. Very similar to the #Sly7_7 answer.
To get a basic model use:
ember g initializer component-store-injector
Then edit this to:
// app/initializers/component-store-injector.js
export function initialize(container, application) {
application.inject('component', 'store', 'store:main');
}
export default {
name: 'component-store-injector',
initialize: initialize
};
I believe this will add the store to all components.
Stolen from https://github.com/ember-cli/ember-cli-todos
I don't know if components are intended to be used such a way. But if you want, I think you can declare an initializer and inject the store into all components.
Ember.onLoad('OlaApp', function(OlaApp) {
OlapApp.initializer({
name: 'injectStoreIntoComponents',
before: 'registerComponents',
initialize: function(container, application){
container.register('store:main', App.Store);
container.injection('component', 'store', 'store:main');
}
})
});
Here is a contrived but working example: http://jsbin.com/AlIyUDo/6/edit
The store can be injected with help of dependency injection.
Example
import Ember from 'ember';
export default Ember.Component.extend({
/**
*
*/
store: Ember.inject.service(),
/**
* Initialize the component.
*/
init() {
this.initialize();
this._super();
},
/**
* Initialize the properties and prerequisites.
*/
initialize() {
// Set the component properties
this.todos().then((data) => {
this.set('todoEntries', data);
});
},
/**
* Returns the todo entries.
*
* #returns {*|Promise|Promise.<T>}
*/
todos() {
const store = this.get('store');
return store.findAll('todo');
},
});
Another way which no one has yet mentioned is to simply pass controller.store to the component e.g.
{{my-awesome-component store=controller.store}}