I'm having some sorting issues inside one of my components and cannot seem to figure it out. Currently it seems to be sorting correctly, but it's putting what should be the 2nd one sorted, at the bottom. Here is my component, hoping someone could give some insight here...Thanks.
import Component from '#ember/component';
import { inject as service } from '#ember/service';
import EmberObject, { computed, observer } from '#ember/object';
export default Component.extend({
googleMapsApi: service(),
geolocation: service(),
sortDefinition: ['distanceTo'],
sortedVineyards: Ember.computed.sort('model', 'sortDefinition'),
didInsertElement() {
this.send('distanceFrom');
},
actions: {
distanceFrom(){
let distanceFromLoading = this.get('distanceFromLoading');
let userLocation = this.get('userLocation');
var userLocationLat = userLocation[0];
var userLocationLon = userLocation[1];
let userLocationFormat = '' + userLocationLat + ',' + userLocationLon;
// console.log(userLocationFormat);
var self = this;
let model = this.get('model');
// console.log(model);
model.forEach(function(item) {
let endLocation = '' + item.get('location');
self._super(...arguments);
self.get('googleMapsApi.google').then((google) => {
var self = this;
let distanceMatrixService = new google.maps.DistanceMatrixService();
function calculateDistance() {
distanceMatrixService.getDistanceMatrix({
origins: [userLocationFormat],
destinations: [endLocation],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.IMPERIAL,
avoidHighways: false,
avoidTolls: false
}, callback);
}
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
} else {
if (response.rows[0].elements[0].status === "ZERO_RESULTS") {
} else {
var distance = response.rows[0].elements[0].distance;
var distance_text = distance.text;
item.set('distanceTo', distance_text);
}
}
}
calculateDistance();
});
});
}
}
});
Turns out in my example, distance_text (the sort definition) was a string. Given my small data set it looked like it was half sorting, when likely it wasn't sorting at all. Turned that number into a proper integer and everything worked nicely.
Related
If I use a transitionTo on a route with a slow model hook, the loading.hbs state never gets triggered (I have loading.hbs files at all of the levels -- cluster, cluster.schedule and cluster.schedule.preview_grid). I tried renaming the one at cluster.schedule preview_grid-loading.hbs with no luck.
On the transitionTo, there is no model or model id passed in, just the route:
viewPreviewGrid: function() {
this.transitionTo('cluster.schedule.preview_grid');
},
I also have a loading action defined as follows:
loading(transition) {
var controller = this.controller;
if (!Ember.isNone(controller)) {
this.controller.reset();
}
transition.promise.finally(function() {
NProgress.done();
});
}
During the transitionTo call the page just stays on the previous route until the promises in the model hook resolve, and then it transitions to the other route. If I refresh the page, the loading state gets triggered just fine. Is this a known behaviour for transitionTo?
This is my model hook:
model: function (/*params*/) {
var socialProfile = this.modelFor('cluster.schedule').get('firstObject');
if (!socialProfile.get('isInstagram')){
throw new Error("Attempted to access preview with non-ig profile: " + socialProfile.get('id'));
}
var accessToken = socialProfile.get('token');
var self = this;
return Ember.RSVP.hash({
igPosts: new Ember.RSVP.Promise(function(resolve) {
self.getUsersRecentMedia(accessToken).then(function(response) {
var igPosts = Ember.A([]);
response.data.forEach(function(data) {
igPosts.pushObject(self.igPostFromResponse(data, socialProfile));
});
resolve(igPosts);
});
}),
posts: new Ember.RSVP.Promise(function(resolve) {
self.store.query('gram', { type: 'preview', social_profile_id: socialProfile.get('id'), limit: self.get('postLimit') }).then(function(grams) {
var filteredGrams = grams.filter(function(gram) {
return (gram.get('scheduledInFuture')) && (gram.belongsTo('socialProfile').id() === socialProfile.get('id')) && (gram.get('active'));
});
resolve(filteredGrams);
});
}),
igUser: new Ember.RSVP.Promise(function(resolve) {
self.getSelf(accessToken).then(function(response) {
resolve(self.igUserFromResponse(response.data, socialProfile));
});
})
});
},
You need to return true at the end of the loading() hook to tell Ember to go ahead and show the default loading route (loading.hbs).
loading(transition) {
var controller = this.controller;
if (!Ember.isNone(controller)) {
this.controller.reset();
}
transition.promise.finally(function() {
NProgress.done();
});
return true;
},
I have a problem calling a function outside action functions from an actions function. As you can see from the code below, I have a selectClient action that calls two functions, createCompanyAccount and createPrivateAccount. But I always get a this.createPrivateAccount is undefined. I have tried using self, but to no avail. Weirdly, I thought I would have to use self.createCompanyAccount, but then I get a self.createCompanyAccount is not defined.
I use Ember 2.12 and Ember Data 2.16.3.
import Ember from 'ember';
export default Ember.Component.extend({
store: Ember.inject.service(),
tagName: '',
/**
* Actions
*/
actions: {
// Select from selectList
selectClient(element) {
let self = this;
if (element.company) {
this.get('store').query('account', { 'filter' : {'orgnumber': element.orgNumber}}).then(
(accounts) => {
/* Organisation exist already */
},
(error) => {
let code = Number(error.errors[0].status);
if (code === 404) {
// company does not exist, so lets create it, and an account.
this.createCompanyAccount(element).then(
(account) => {
/* Do stuff... */
}
);
}
}
);
} else {
this.createPrivateAccount(element).then(
(anonUser) => {
/* Do stuff... */
}
);
}
}
},
createCompanyAccount(company) {
let account = this.get('store').createRecord('account', {
type: 'company',
});
// Check if postal address is set on result
if (typeof company.addressObject !== 'undefined') {
let postAddress = this.get('store').createRecord('address', {
address: company.addressObject.streetName,
zip: company.addressObject.zip,
postal_address: company.addressObject.postalAddress
});
account.get('addresses').pushObject(postAddress);
}
this.get('store').createRecord('company', {
name: company.name,
org_number: Number(company.orgNumber),
account: account
}).save().then((new_company) => {
return new_company.get('account');
});
},
createPrivateAccount(person) {
let account = this.get('store').createRecord('account', {
type: 'anonuser'
});
// Check if postal address is set on result
if (typeof person.addressObject !== 'undefined') {
let postAddress = this.get('store').createRecord('address', {
address: person.addressObject.streetName,
zip: person.addressObject.zip,
postal_address: person.addressObject.postalAddress
});
account.get('addresses').pushObject(postAddress);
}
this.get('store').createRecord('anonUser', {
first_name: person.firstName,
sur_name: person.surName,
email: person.email,
phone: person.phone,
account: account,
}).save().then((new_person) => {
return new_person.get('account');
});
}
});
Can anyone see where I go wrong? I can note that there is a few other functions that I have removed for clarity.
Thank you,
Tommy
Your issue is not about this.createPrivateAccount and this.createCompanyAccount being undefined. I think both of them are being executed but they do return undefined but you are expecting a Promise. Therefore this.createPrivateAccount().then is undefined.
I have a service for a business calendar where a property is not available to a computed property but is available in a method.
In the code below the property calendars is not defined when the computed businessYear property is accessed. On the other hand, calendars is defined when the year() method is called.
The currentYear property is used elsewhere so init() is called and the store promise is resolved before businessYear is accessed.
I've debugged this and developer tools shows that this.calenders is defined in watch variables but is still undefined in the computed property. It's almost like the computed property is being executed in another context. I'm probably missing something really basic but just don't see it.
app/services/business-calendar.js
import Ember from 'ember';
import moment from 'moment';
const { computed, inject: { service }, get, set } = Ember;
const findCalendar = (date, calendars) => {
let p1, p13
return calendars.find(function (item, index, enumerable) {
p1 = moment(get(item, 'p1'));
p13 = moment(get(item, 'p13'));
return moment.range(p1, p13).contains(date);
})
}
export default Ember.Service.extend({
store: service(),
calendars: [],
currentDate: computed(() => {
return moment();
}),
currentYear: computed('currentDate', () => {
return moment(get(this, 'currentDate')).year();
}),
businessYear: computed(() => {
let calendar = findCalendar(moment(), get(this, 'calendars'));
return get(this.calendar, 'year');
}),
init() {
this._super(...arguments);
get(this, 'store').findAll('calendar').then((recs) => {
set(this, 'calendars', recs);
})
},
year(date) {
let d = moment(date);
var calendar = findCalendar(d, get(this, 'calendars’));
return get(calendar, 'year');
}
});
Thanks to feedback on the Ember Slack feed the issue is the use of arrow functions in the computed properties. This creates a different 'this' or context. The working code is below. Note even thought the currentDate and currentYear computed properties were working, all arrow functions were removed for code consistency.
import Ember from 'ember';
import moment from 'moment';
const { computed, inject: { service }, get, set } = Ember;
const findCalendar = (date, calendars) => {
let p1, p13
return calendars.find(function (item, index, enumerable) {
p1 = moment(get(item, 'p1'));
p13 = moment(get(item, 'p13'));
return moment.range(p1, p13).contains(date);
})
}
export default Ember.Service.extend({
store: service(),
calendars: [],
currentDate: computed(function() {
return moment();
}),
currentYear: computed('currentDate', function() {
return moment(get(this, 'currentDate')).year();
}),
businessYear: computed(function() {
let calendar = findCalendar(moment(), get(this, 'calendars'));
return get(this.calendar, 'year');
}),
init() {
this._super(...arguments);
get(this, 'store').findAll('calendar').then((recs) => {
set(this, 'calendars', recs);
})
},
year(date) {
let d = moment(date);
var calendar = findCalendar(d, get(this, 'calendars’));
return get(calendar, 'year');
}
});
I have following in my controller, and facing issue while updating property with array change..
import Ember from 'ember';
export default Ember.Controller.extend({
imageIds: Object.keys(JSON.parse(localStorage.image_ids || "{}")),
// imageIds = ['gnffffffffjdf', 'hzfyfsidfulknm', 'euriekjhfkejh']
previewImageId: function() {
return this.imageIds.get('firstObject');
}.property('imageIds.[]'),
actions: {
addDetails: function() {
this.transitionToRoute('items.add_item');
},
removeImage: function(image_id) {
var uploaded = JSON.parse(localStorage.image_ids || "{}");
delete uploaded[image_id]
localStorage.image_ids = JSON.stringify(uploaded);
this.get("imageIds").removeObject(image_id);
// this.set("imageIds", Object.keys(JSON.parse(localStorage.image_ids || "{}")));
},
updatePreview: function(image_id){
this.set("previewImageId", image_id);
var uploaded = JSON.parse(localStorage.image_ids || "{}");
uploaded[image_id] = image_id;
localStorage.image_ids = JSON.stringify(uploaded);
// this.set("imageIds", Object.keys(JSON.parse(localStorage.image_ids)));
this.get("imageIds").pushObject(image_id);
}
},
init: function(){
var controller = this;
Ember.$('body').on('click', ".current_image", function() {
var public_id = Ember.$(this).attr('id');
controller.set("previewImageId", public_id);
});
}
});
Whenever there is any change in the imageIds array, previewImageId should be updated.
tried using pushObject, removeObject, .get and .set options.
But still no luck
Can anyone pls help me?
ANSWER:
import Ember from 'ember';
export default Ember.Controller.extend({
imageIds: function() {
return Object.keys(JSON.parse(localStorage.image_ids || "{}"));
}.property(),
previewImageId: function() {
return this.get("imageIds.firstObject");
}.property('imageIds.[]'),
actions: {
addDetails: function() {
this.transitionToRoute('items.add_item');
},
removeImage: function(image_id) {
var uploaded = JSON.parse(localStorage.image_ids || "{}");
delete uploaded[image_id]
localStorage.image_ids = JSON.stringify(uploaded);
this.get("imageIds").removeObject(image_id);
},
updatePreview: function(image_id){
var uploaded = JSON.parse(localStorage.image_ids || "{}");
uploaded[image_id] = image_id;
localStorage.image_ids = JSON.stringify(uploaded);
this.get("imageIds").unshiftObject(image_id);
}
},
init: function(){
var controller = this;
Ember.$('body').on('click', ".current_image", function() {
var public_id = Ember.$(this).attr('id');
controller.get("imageIds").removeObject(public_id);
controller.get("imageIds").unshiftObject(public_id);
});
}
});
Here previously I tried with setting value to previewImageId.. which was wrong way, as it overrides my computed property.
I could see that you are setting the previewImageId cp in a couple of places. You should make the computed property as a setter and getter aware.
Take a look here for an example
If the cp is implemented without a setter, then setting some value on the cp will overwrite its computed function.
Here is a working demo for your use case.
Basically I made the imageIds a property. Here is the code snippet:
App.IndexController = Ember.ArrayController.extend({
imageIds: function() {
return this.get("content");
}.property(),
previewImageId: function() {
return this.get("imageIds").get("firstObject");
}.property("imageIds.[]"),
actions: {
remove: function(item) {
this.get("imageIds").removeObject(item);
}
}
});
Hope this helps!
I have two models:
App.User = DS.Model.create({
comments: DS.hasMany('App.Comment')
});
App.Comment = DS.Model.create({
user: DS.belongsTo('App.User')
});
When a user is deleted, it also will delete all its comments on the backend, so I should delete them from the client-side identity map.
I'm listing all the comments on the system from another place, so after deleting a user it would just crash.
Is there any way to specify this kind of dependency on the association? Thanks!
I use a mixin when I want to implement this behaviour. My models are defined as follows:
App.Post = DS.Model.extend(App.DeletesDependentRelationships, {
dependentRelationships: ['comments'],
comments: DS.hasMany('App.Comment'),
author: DS.belongsTo('App.User')
});
App.User = DS.Model.extend();
App.Comment = DS.Model.extend({
post: DS.belongsTo('App.Post')
});
The mixin itself:
App.DeletesDependentRelationships = Ember.Mixin.create({
// an array of relationship names to delete
dependentRelationships: null,
// set to 'delete' or 'unload' depending on whether or not you want
// to actually send the deletions to the server
deleteMethod: 'unload',
deleteRecord: function() {
var transaction = this.get('store').transaction();
transaction.add(this);
this.deleteDependentRelationships(transaction);
this._super();
},
deleteDependentRelationships: function(transaction) {
var self = this;
var klass = Ember.get(this.constructor.toString());
var fields = Ember.get(klass, 'fields');
this.get('dependentRelationships').forEach(function(name) {
var relationshipType = fields.get(name);
switch(relationshipType) {
case 'belongsTo': return self.deleteBelongsToRelationship(name, transaction);
case 'hasMany': return self.deleteHasManyRelationship(name, transaction);
}
});
},
deleteBelongsToRelationship: function(name, transaction) {
var record = this.get(name);
if (record) this.deleteOrUnloadRecord(record, transaction);
},
deleteHasManyRelationship: function(key, transaction) {
var self = this;
// deleting from a RecordArray doesn't play well with forEach,
// so convert to a normal array first
this.get(key).toArray().forEach(function(record) {
self.deleteOrUnloadRecord(record, transaction);
});
},
deleteOrUnloadRecord: function(record, transaction) {
var deleteMethod = this.get('deleteMethod');
if (deleteMethod === 'delete') {
transaction.add(record);
record.deleteRecord();
}
else if (deleteMethod === 'unload') {
var store = this.get('store');
store.unloadRecord(record);
}
}
});
Note that you can specify via deleteMethod whether or not you want to send the DELETE requests to your API. If your back-end is configured to delete dependent records automatically, then you will want to use the default.
Here's a jsfiddle that shows it in action.
A quick-and-dirty way would be to add the following to your user model
destroyRecord: ->
#get('comments').invoke('unloadRecord')
#_super()
I adapted the answer of #ahmacleod to work with ember-cli 2.13.1 and ember-data 2.13.0. I had an issue with nested relationships and the fact that after deleting an entity from the database its id was reused. This lead to conflicts with remnants in the ember-data model.
import Ember from 'ember';
export default Ember.Mixin.create({
dependentRelationships: null,
destroyRecord: function() {
this.deleteDependentRelationships();
return this._super()
.then(function (model) {
model.unloadRecord();
return model;
});
},
unloadRecord: function() {
this.deleteDependentRelationships();
this._super();
},
deleteDependentRelationships: function() {
var self = this;
var fields = Ember.get(this.constructor, 'fields');
this.get('dependentRelationships').forEach(function(name) {
self.deleteRelationship(name);
});
},
deleteRelationship (name) {
var self = this;
self.get(name).then(function (records) {
if (!records) {
return;
}
var reset = [];
if (!Ember.isArray(records)) {
records = [records];
reset = null;
}
records.forEach(function(record) {
if (record) {
record.unloadRecord();
}
});
self.set(name, reset);
});
},
});
Eventually, I had to set the relationship to [] (hasMany) or null (belongsTo). Else I would have run into the following error message:
Assertion Failed: You cannot update the id index of an InternalModel once set. Attempted to update <id>.
Maybe this is helpful for somebody else.