Set a different controller's model via action doing a fetch query - ember.js

I am trying to set the content of an another controller but my model returns undefined. I've tried everything I could think of to get the queried results prior to trying to set the other controller's model.
Mapmaker.CategoriesController = Ember.ArrayController.extend({
needs: ['filters'],
actions: {
setCategories: function(item) {
var content = this.getFilters(item.id);
console.log(content.fulfillmentValue._data.objects);
this.get("controllers.filters").set('model', content.fulfillmentValue._data.objects);
}
},
getFilters: function(id){
//trying to force sync
return Mapmaker.Tile.fetch('?categories=' + id);
}
});
Any thoughts? let me know if I need to show more code.
I am using ember-model's restful adapter to query the results.
I am getting results but they are just isLoaded:false the second I try to set the controller's model.

fetch in ember model returns a promise, not a model, use the promise
var promise = this.getFilters(item.id);
promise.then(function(content){
console.log(content);
this.get("controllers.filters").set('model', content);
}

Related

Passing data from controller to model in emberjs

I have a controller where I get the value from the hbs, which sends me the selected country value. I need this selected country in the model to compute and return back some results back to the hbs. How set this value in controller and get it in the model so I can compute using that value?
Well, there may be some different approaches to achieve this. However, I will give you some example which will hopefully help you.
//Controller.js
notes: Ember.computed('model.notes.[]', 'model.notes.#each.date', function() {
return this.get('model.notes').sortBy('date').reverse(); //This is an example of Computed function which in this case it's sorting notes based on date.
}),
blink: null,
actions: {
taskChangeColor: function() {
this.set('blink', 'blinker'); // this is another example that set new data by action which can be retrive from model and set to property
}
}
or another thing that you can do is to use Computed function in Model itself like
// model.js which is using ember-data and moment
timeZone: DS.attr(), //for example one property coming from server
utcOffsetFormat: Ember.computed(function() {
let time = moment.tz(this.get('timeZone')).format('hh:mm a');
return time;
// using a computed function to instantiate another value based on existing model property which means you can simpley use this property instead of direct one.
})
Additionally, you still are eligible to use action in Route.js instead of controller an example would be :
//route.js
actions: {
changeSave: function(step) {
var something = {
contact: this.currentModel,
};
this.currentModel.set('step', something.contact);
this.currentModel.save().then(d => {
// set your alert or whatever for success promise
return d;
}).catch(e => {
console.log(error(e.message));
return e;
});
},
in above example you can see that I have set an action to save notes in model which easily can set() to the model with exact same property name and if you do this you will get the result back immediately in your view.
hope it can help you. I recommend to read Ember-Docs
I would say, for your requirement you don't need controller properties for selectedCountryValue. You can keep this value in model itself.
In route,
setupController(model,transition){
this._super(...arguments); //this will set model property in controller.
Ember.set(model,'selectedCountryValue','US'); //you can set default value
}
and inside controller, you create computed property with dependent on model.selectedCountryValue. and compute some results
result:Ember.Computed('model.selectedCountryValue',function(){
//compute something return the result
}
In template, you can use {{model.selectedCountryValue}} directly.

Ember data - computed property not firing when model is updated with createRecord

For reasons beyond the scope of this question, I have to populate an Ember data model named Activity in my SearchRoute using Ember.$.getJSON in the model hook like this:
App.SearchRoute = Ember.Route.extend({
model: function (params) {
// Create a promise to return to the model hook. The promise will return a DS.RecordArray.
var modelPromise = new Ember.RSVP.Promise(function (resolve, reject) {
// Make the AJAX call to retrieve activities that match the search criteria
Ember.$.getJSON('/services/activities?query=' + params.q).then(function (data) {
data.activities.forEach(function (activity) {
// If the current activity does not already exist in the store...
if (!this.store.hasRecordForId('activity', activity.id)) {
// add the activity to the store
this.store.createRecord('activity', {
id: activity.id,
title: activity.propertyBag.title
});
}
}.bind(this));
resolve(this.store.all('activity', { query: params.q }));
}.bind(this));
}.bind(this));
// Return the DS.RecordArray as the model for the search route
return modelPromise;
}
});
Then, in my SearchController I do some model sorting and filtering before returning the results from a computed property that is bound to a template that displays the results, like this:
App.SearchController = Ember.ArrayController.extend({
filteredActivities: function () {
var model = this.get('model');
// complete various model sorting and filtering operations
return model;
}.property('model')
});
Here's the template:
<script type="text/x-handlebars" data-template-name="activities">
{{#each item in filteredActivities}}
{{item.title}}
{{/each}}
</script>
Every time a search is executed, the model hook in the SearchRoute is refreshed, the AJAX request is made, and the store is updated with new Activity records, if necessary.
The problem is, even if I do create new records in the store using createRecord and return the new store query results to my model hook, the filteredActivities property does not get fired and the template does not update.
I would think that because I'm returning a newly updated DS.RecordArray to the model hook, that Ember would consider my model as having changed and fire any computed properties watching for changes to the model, but I must be missing something.
Does anybody have any ideas?
Sorry for the long post, and thank you so much for taking the time to consider my issue!
Don't use createRecord. Use push.
http://guides.emberjs.com/v1.10.0/models/pushing-records-into-the-store/
Do you try model.[] or model.#each.propertyNameToObserve in computed property filteredActivities?
Examples with #each: http://guides.emberjs.com/v1.10.0/object-model/computed-properties-and-aggregate-data/,
http://guides.emberjs.com/v1.10.0/controllers/representing-multiple-models-with-arraycontroller/

Index view not refreshing after receiving updated data from backend

I am testing my application, so I am doing the following:
I show an index view (#/locators/index), of Locator objects, which I initially load with App.Locator.find();
I modify the backend manually
Manually (with a button/action) I trigger a refresh of the data in the ember frontend, without changing the route. I do this with App.Locator.find().then(function(recordArray) {recordArray.update();});. I see via console logging that a list request is sent to the backend, and that the up-to-date data is received. I assume this is used to update the store.
BUT: The view does not update itself to show this new data
Why does the view not get automatically updated when the store receives new data? Isn't that the whole point of the data binding in Ember?
If I now do the following:
Open any other route
Go back to the locators index route (#/locators/index)
Ember sends a new request to list the locators
The index view is shown, with the correct data (since it was already in the store?)
New data is received
(I am not 100% sure that 4 and 5 happen in that order, but I am quite certain)
So, my impression is that the data is properly updated in the store, but that somehow a full re-rendering of the view is needed to display this new data, for example by leaving and re-entering the route. Is this true? Can I force this re-rendering programmatically?
Ember changes view data when the underlying model is changed by the controller(Which is binded to the view)
(Only when the state of the application changes(url changes) router hooks are called)
Your problem could be solved when you do this.refesh() inside your route by capturing the action triggered by your view.
App.IndexRoute = Ember.Route.extend({
actions: {
dataChanged: function() {
this.refresh();
}
},
//rest of your code goes here
});
for this to work your handlebar template which modifies the data shoud have an action called dataChanged
example :
Assume this action is responsible for changing/modifying/deleting the underlying data
<button {{action 'dataChanged'}}> Change Data </button>
Refresh method actually does a model refresh and passes it to the corresponding controller which indeed changes the view.
There a couple of things that come to mind you could try:
If you are inside of an ArrayController force the content to be replaced with the new data:
this.replaceContent(0, recordArray.get('length'), recordArray);
Or try to call reload on every single record trough looping the recordArray:
App.Locator.find().then(function(recordArray) {
recordArray.forEach(function(index, record) {
record.reload();
}
}
And if the second approach works, you could also override the didLoad hook in your model class without having to loop over them one by one:
App.Locator = DS.Model.extend({
...
didLoad: function(){
this.reload();
}
});
If this works and you need this behaviour in more model classes consider creating a general mixin to use in more model classes:
App.AutoReloadMixin = Ember.Mixin.create({
didLoad: function() {
this._super();
this.reload();
}
});
App.Locator = DS.Model.extend(App.AutoReloadMixin, {
...
});
App.Phone = DS.Model.extend(App.AutoReloadMixin, {
...
});
Update in response to your answer
Handlebars.registerHelper is not binding aware, I'm sure this was causing your binding not to fire. You should have used Handlebars.registerBoundHelper or simply Handlebars.helper which is equivalent:
Handlebars.helper('grayOutIfUndef', function(property, txt_if_not_def) {
...
});
Hope this helps.
Somehow this seems to be due to the fact that I am using custom handlebar helpers, like the following:
Handlebars.registerHelper('grayOutIfUndef', function(property, txt_if_not_def) {
// HANDLEBARS passes a context object in txt_if_not_def if we do not give a default value
if (typeof txt_if_not_def !== 'string') { txt_if_not_def = DEFAULT_UNDEFINED_STR; }
// If property is not defined, we return the grayed out txt_if_not_def
var value = Ember.Handlebars.get(this, property);
if (!value) { value = App.grayOut(txt_if_not_def); }
return new Handlebars.SafeString(value);
});
Which I have been using like this:
{{grayOutIfUndef formattedStartnode}
Now I have moved to a view:
{{view App.NodeIconView nodeIdBinding="outputs.startnode"}}
Which is implemented like this:
App.NodeIconView = Ember.View.extend({
render: function(buffer) {
var nodeId = this.get('nodeId'), node, html;
if (nodeId) {
node = App.getNode(nodeId);
}
if (node) {
html = App.formattedLabel.call(node, true);
} else {
html = App.grayOut(UNDEFINED_NODE_NAME);
}
return buffer.push(html);
}
});
I am not sure why, but it seems the use of the custom handlebars helper breaks the property binding mechanism (maybe my implementation was wrong)

transition after saving model of ember data

I want to make transition after a create a post.
post/new > click submit > rails backend successfully create post and response a json > redirect to newly created post's path
in ember_data_example github source code. they use this approach
transitionAfterSave: function() {
// when creating new records, it's necessary to wait for the record to be assigned
// an id before we can transition to its route (which depends on its id)
if (this.get('content.id')) {
this.transitionToRoute('contact', this.get('content'));
}
}.observes('content.id'),
It works fine, because The model has ID of null when model created, and its ID would change when model saving is successful because this function observes change of models ID.
But maybe, this function will be executed whenever model's ID property is changed.
I'm finding some more semantic way.
I want transition to be executed
when the model's status is changed to 'isDirty' = false && 'isNew' == true form 'isDirty' = true, 'isNew' = false.
How can I implement this?
Ideally, the id is not supposed to change. However, you are correct, semantically, this method doesn't seem right.
There is a cleaner way to do this:
save: function(contact) {
contact.one('didCreate', this, function(){
this.transitionToRoute('contact', contact);
});
this.get('store').commit();
}
UPDATE 2013-11-27 (ED 1.0 beta):
save: function(contact) {
var self = this;
contact.save().then(function() {
self.transitionToRoute('contact', contact);
});
}
Note for Ember 2.4 It is encoraged to handle saving actions in the component or route level (and avoid controllers). Here's an example below. Note the id on the model object in the transition. And note how we use transitionTo and not transitionToRoute in the route.
actions: {
save() {
var new_contact = this.modelFor('contact.new');
new_contact.save().then((contact) => {
this.transitionTo('contact.show', contact.id);
});
},
actions: {
buttonClick: function () {
Ember.debug('Saving Hipster');
this.get('model').save()
.then(function (result) {
this.transitionToRoute('hipster.view', result);
}.bind(this));
}
}

How to get entrie from another controllers content and call self controller on missing

This is code illustration:
http://jsbin.com/uzapuy/1/edit
I want to be able to access test by URL like so: http://jsbin.com/uzapuy/1#/test/2
Because entrie may already present in client side, I want to check that first and only if it missing fetch from server side.
Is that possible without DS.Store?
In your TestRoute, can you not do something like:
App.TestRoute = Ember.Route.extend({
model: function(params) {
// Find your controller that has the "fetch" method.
var testController = this.controllerFor('test');
// Check if there's an existing model with this ID.
var existingModel = testController.find('id', params.id);
// Determine if we found a model or not.
if (existingModel) {
// If we found an existing model, then we can set this as the model.
return existingModel;
}
// Otherwise we'll fetch it from the server.
return testController.fetch(params.id);
}
});
Also, bear it in mind that in your code, the test variable is undefined:
var tests = App.Test.create({id: id, name: 'fetched ' + id});
self.set('content', test);
You're after tests, I assume.