Ember: Consume Service in Component - ember.js

I simply want to display my longitude on my app landing page/index/application.hbs. I am embarrassed to say how long I have been working at this! Can anyone help me?
//geoservice.js
import Ember from 'ember';
export default Ember.Service.extend({
longitude: function(position){
return position.coords.longitude;
},
latitude: function(position){
return position.coords.latitude;
}
});
//geo-component.js
import Ember from 'ember';
export default Ember.Component.extend({
geoservice: Ember.inject.service(),
myLongitude: function(){
if (navigator.geolocation) {
return this.get('geoservice').longitude(navigator.geolocation.getCurrentPosition());
} else {
return "Geolocation is not supported by this browser.";
}
}.on('init'),
});
//application.hbs
<h2 id="title">Welcome to Ember</h2>
{{outlet}}
{{geo-component.myLongitude}}

The problem wasn't consuming a service in a component, but that 1) the Geolocation API is asynchronous, and 2) you don't render a component like you did. This works:
app/components/geo-location.js
import Ember from 'ember';
export default Ember.Component.extend({
geo: Ember.inject.service(),
loading: true,
error: null,
latitude: null,
longitude: null,
setPosition: Ember.on('init', function() {
this.get('geo').getPosition().then((position) => {
this.set('latitude', position.latitude);
this.set('longitude', position.longitude);
this.set('loading', false);
}).catch((error) => {
this.set('error', error);
});
})
});
app/services/geo.js
import Ember from 'ember';
export default Ember.Service.extend({
getPosition() {
return new Ember.RSVP.Promise((success, error) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(success, error)
} else {
error( new Error("Geolocation is not supported by this browser.") );
}
}).then((position) => {
return { latitude: position.coords.latitude, longitude: position.coords.longitude };
});
}
});
app/templates/components/geo-location.hbs
{{#if error}}
{{error}}
{{else if loading}}
Loading...
{{else}}
lat: {{latitude}}, long: {{longitude}}
{{/if}}
app/templates/application.hbs
<h2 id="title">Welcome to Ember</h2>
{{geo-location}}

Related

Need to show/hide a button depending on the page

I am trying to hide back button on site-header that takes me to dashboard. I am using pod structure that is something like this:
pod
component
site-header
template.hbs
component.js
main
dashboard
In the component.js I used computed to get current route
import Component from '#ember/component';
import { inject as service } from '#ember/service';
import { computed } from '#ember/object';
export default Component.extend({
router: service (),
dashboard:computed('currentRouteName',function(){
if(this.get('currentRouteName') === 'main.dashboard.index'){
return true;
}
return false;
})
})
In template.hbs I used the following code to check the link.
{{#unless dashboard}}
{{#link-to "main.dashboard" class="back-btn"}}{{t "goBackToDashboard"}}{{/link-to}}
{{/unless}}
Still it is the same by tweaking the if/else conditions also I either get the button on all pages or on none.
Any help will be appreciated.
app/route.js:
import EmberRouter from '#ember/routing/router';
import config from './config/environment';
import { inject } from '#ember/service';
import $ from 'jquery';
const Router = EmberRouter.extend({
location: config.locationType,
rootURL: config.rootURL,
ajax: inject('ajax'),
});
Router.map(function () {
this.route('login', { path: 'login' });
this.route('main', { path: '/' }, function () {
this.route('dashboard', { path: '' }, function () {});
this.route("review", { path: "/review/:docId" }, function() { // eslint-disable-line
this.route("edit", { path: "/edit/:bitId" }); // eslint-disable-line
this.route('window_edit');
});
}
You mention that the computed property is in the component.js, and you are doing this.get('currentRouteName'), but that property does not exist in components.
I believe you need to use the router service in your component.
I'm assuming you are using pre-Octane syntax, so it should look something like this:
import Component from '#ember/component';
import { inject as service } from '#ember/service';
import { computed } from '#ember/object';
export default Component.extend({
router: service(),
dashboard: computed('router.currentRouteName',function() {
if (this.get('router.currentRouteName') === 'main.dashboard.index') {
return true;
}
return false;
})
});
I don't remember which version RouterService was first available, but I hope this helps!

Need some guidance on Ember component functionality

I'm currently leveraging ember-cli-geolocate along with ember-google-maps to provide users with the closest point of interest to their current location. I've got the code working in a component but have now realized that I'm not able to sort.
Code below:
//routes/vineyard/index.js
import Ember from 'ember';
import { later } from '#ember/runloop';
import { inject as service } from '#ember/service';
import $ from 'jquery';
const { Route, set } = Ember;
export default Route.extend({
model() {
return this.store.findAll('vineyard');
},
setupController(controller, model) {
set(controller, 'vineyards', model);
},
activate() {
this.controllerFor('vineyard/index').send('distanceFrom');
}
});
//controllers/vineyard/index.js
import Ember from 'ember';
import { inject as service } from '#ember/service';
import $ from 'jquery';
export default Ember.Controller.extend({
userLocation: null,
endLocation: null,
milesAway: null,
locationIsLoading: true,
failState: false,
googleMapsApi: service(),
geolocation: service(),
panelActions: Ember.inject.service(),
userLocationChanged: function () {
this.get('userLocation');
this.toggleProperty('locationIsLoading');
}.observes('userLocation'),
actions: {
distanceFrom: function() {
this.get('geolocation').trackLocation().then((geoObject) => {
let currentLocation = this.get('geolocation').get('currentLocation');
this.set('userLocation', currentLocation);
}, (reason) => {
// this.toggleProperty('failState');
// $('.error').css('height', '220px');
// $('.error > p').css('height', 'auto');
console.log('Geolocation failed because ' + reason);
});
},
stopError: function() {
this.toggleProperty('failState');
$('.error').css('height', '0');
$('.location-loader').animate({opacity: '0'}, 1000);
}
},
});
components/miles-away.js
import Component from '#ember/component';
import { inject as service } from '#ember/service';
import { later } from '#ember/runloop';
import $ from 'jquery';
export default Component.extend({
googleMapsApi: service(),
geolocation: service(),
userLocation: null,
endLocation: null,
milesAway: null,
distanceLoading: true,
errorState: false,
fadeClass: '',
didInsertElement() {
this.set('self', this);
var address = this.get('address');
var location = this.get('location');
var distanceLoading = this.get('distanceLoading');
var userLocationLat = location[0];
var userLocationLon = location[1];
let userLocation = '' + userLocationLat + ',' + userLocationLon
this.set('userLocation', userLocation);
this.set('endLocation', address);
this.send('getDistance');
},
actions: {
getDistance: function() {
// let milesAway = this.get('milesAway');
let userLocation = this.get('userLocation');
let endLocation = this.get('endLocation');
this._super(...arguments);
this.get('googleMapsApi.google').then((google) => {
var self = this;
let distanceMatrixService = new google.maps.DistanceMatrixService();
function calculateDistance() {
distanceMatrixService.getDistanceMatrix({
origins: [userLocation],
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) {
self.toggleProperty('errorState');
} else {
// var origin = response.originAddresses[0];
// var destination = response.destinationAddresses[0];
if (response.rows[0].elements[0].status === "ZERO_RESULTS") {
self.toggleProperty('errorState');
} else {
var distance = response.rows[0].elements[0].distance;
// var distance_value = distance.value;
var distance_text = distance.text;
// const miles = distance_text.substring(0, distance_text.length - 3);
self.toggleProperty('distanceLoading');
self.set('milesAway', distance_text);
later((function() {
$('.miles-away').addClass('fade-in');
}), 45);
}
}
}
calculateDistance();
});
},
}
});
components/miles-away.hbs
{{#unless distanceLoading}}
<div class="miles-away-container">
<p class="miles-away">{{milesAway}}</p>
</div>
{{/unless}}
{{yield}}
and finally, the template in which this is rendered..(just providing a snippet)
templates/vineyard/index.hbs
<div class="distance">
{{#if failState}}
{{svg-jar 'fail'}}
{{/if}}
{{#if locationIsLoading}}
{{location-loader}}
{{else}}
{{miles-away location=userLocation address=vineyard.location}}
{{/if}}
</div>
I'm open to implementing this in a completely different way, I know it's not even close to proper or perfect.

Empty model in Route

I have the following route:
import Ember from 'ember';
export default Ember.Route.extend({
ajax: Ember.inject.service(),
queryParams: {
search: {
refreshModel: true
}
},
beforeModel: function() {
var params = this.paramsFor('recipes');
var that = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
that.get('ajax').request({
url: "/recipes",
method: "GET",
data: {
namePart: params.search
}
},
function(response) {
that.store.unloadAll("recipe");
response.forEach(function(item) {
that.store.push(that.store.normalize("recipe", item));
});
resolve();
});
});
},
model: function() {
this.store.peekAll('recipe');
}
});
And controller:
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams: ['search'],
search: null
});
The request is successful. And I even see appropriate data in the store. But route/controller model is null. What I'm doing wrong?
You're missing return keyword in model:
model() {
return this.store.peekAll('recipe');
}

Ember: count number of keys in object?

I have data that looks roughly like this:
"id": "1",
"slug": "WD",
"name": {
"en": "Working Draft",
"de": "Arbeitsentwurf",
"fr": "Version de travail",
"ja": "草案",
"ru": "Рабочий черновик"
}
And I am passing the name object to a component:
{{title-name name=model.name lang='en'}}
In the component template I would like to output the number of translations
<p>Translated {{translationCount}} times.</p>
I tried a few different things in my component to come up with this total but none of them work. How would I count the number of objects?
export default Ember.Component.extend({
// did not work:
translationCount: Ember.computed.alias('Object.keys(name).length'),
// did not work:
// translationCount: Ember.computed.alias('name.length'),
});
Being a little more explicit about it seems to work:
export default Ember.Component.extend({
translationCount: Ember.computed('name', function() {
return Object.keys('name').length;
})
});
Check out this ember-twiddle for an implementation of this.
Application Template
<h1>Welcome to the {{appName}}</h1>
{{title-name name=data.name lang='en'}}
{{outlet}}
Application Controller
import Ember from 'ember';
export default Ember.Controller.extend({
appName:'Stephanie Hobson App',
data: {
id: 1,
slug: 'WD',
name: {
en: 'Working Draft',
de: 'Arbeitsentwurf',
fr: 'Version de travail',
ja: '草案',
ru: 'Рабочий черновик'
}
}
});
title-name.js component
import Ember from 'ember';
var { computed, get } = Ember;
export default Ember.Component.extend({
translationCount: computed('name', function() {
var name = get(this, 'name');
return Object.keys(name).length;
})
});
title-name.hbs component template
{{yield}}
{{translationCount}}

Ember template renders function as string

I'm new to ember and got a problem with a template.
My route
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function(params) {
var pageNum = params.page || 1,
pageRows = 8;
return this.store.find('account', {
page: pageNum,
rows: pageRows
});
},
setupController: function(controller, model) {
controller.set('model', model);
controller.set('greeting', 'Hello World');
}
});
My Controller
import Ember from 'ember';
export default Ember.ArrayController.extend({
contentLength: function() {
// console.log(this);
// console.log('length: ' + this.get('content').length);
// return this.get('content').length;
return 'Test string';
},
actions: {}
});
Template
{{ greeting }}
{{ contentLength }}
The {{ greeting }} gets rendered correctly. But {{ contentLength }} gets rendered out as a string function..
Hello World function () { // console.log(this); // console.log('length: ' + this.get('content').length); // return this.get('content').length; return 'Test string'; }
Anyone who can help me solve this issue?
Thanks
You need to add .property() at the end of the contentLength function in order to display it in a template:
import Ember from 'ember';
export default Ember.ArrayController.extend({
contentLength: function() {
// console.log(this);
// console.log('length: ' + this.get('content').length);
// return this.get('content').length;
return 'Test string';
}.property(),
actions: {}
});
If you want the property to update whenever another property of your controller changes simply add it as a "parameter" of your property like this .property("thepropertytoobserve") and the length property of an arrayController is already aviable as {{length}} in the template.
Have a look at the doc for more details on computerd properties.
You can just use {{ length }} in your template as ArrayControllers already have that property.
The reason your contentLength function is not doing what you want is because it is not a computed property. You need to either use Ember.computed(function() { ..}) or append .property(...) to your contentLength function.
Eg:
contentLength: function() {
return this.get('content.length');
}.property('content.length')