How to get current route path in helper in Ember? - javascript

I'm creating a helper and I need to access the current route name. I'm using Ember CLI with ES6 and so I don't have access to App object.
Here's what I have.
import Ember from 'ember';
/**
* {{route-active 'route' ['stringIfActive' ['stringIfNot']]}}
*/
export function routeActive(params/*, hash*/) {
var currentRoute = null; // we need this
if( ! params.length ) {
return;
}
return currentRoute === params[0] ?
params[1] || 'active' :
params[2] || '';
}
export default Ember.HTMLBars.makeBoundHelper(routeActive);

With the shift to components, it is harder to get route name. The best way is to add an initializer such as
ember g initializer router
(from command line), and
export function initialize(application) {
application.inject('route', 'router', 'router:main');
application.inject('component', 'router', 'router:main');
}
export default {
name: 'router',
initialize
};
in a initializers/router.js. You can also inject into controller if you need to. Then just do simply
this.get('router.currentRouteName');
in JS, or
{{router.currentRouteName}}
in template.
This is the only way I have found to get it reliably, and observable in Ember 2.4

Although it's a private API, I've been known to use and abuse the container for when I need it. The application controller has the currently active route as a property called currentPath.
import Ember from 'ember';
export default Ember.Handlebars.makeBoundHelper( function(value, options) {
var appCtrl = this.container.lookup("controller:application");
return appCtrl.get('currentPath');
});
This assumes you wanted your helper to simply return the value. If you need to return something else, well at least I got you this far I'm sure you can take it from here.

Related

What is the right way to use `useFactory` and `deps:[ ]` in Angular?

I want to use a simple provider with useFactory which will also use deps (learning purpose) .
So I've created this plnkr :
//app.module.ts
...
const randomFactory = (car,engine) => { return new Calc(car,engine); };
...
providers: [Car,Engine,
{ provide: 'Random',
useFactory: randomFactory ,
deps: [Car, Engine],
},
]
Where Calc is :
export class Calc
{
car:Car;
engine:Engine;
constructor ( Engine engine, Car car)
{
this.car=car;
this.engine=engine;
}
doSomething(){alert(this.car.getInfo() + this.engine.getInfo()}
}
Now I want to use it as an injected object so I did this :
export class AppComponent {
constructor(#Inject('Random') r) {
r.doSomething(); //should alert "car data + engine data" , but I get an error.
}
}
But I get an error : car is not defined
Question:
I don't see any point of using deps:[] If I already declare that modules in the main app module ( and so it is known in the global app) and in the Calc class where I import those classes.
So how can I fix my code to use deps:[] the right way ?
plnkr
The providers configuration is correct. You've got a syntax error in the Calc constructor. The correct declaration of the arguments is as follows:
constructor (engine: Engine, car: Car){
Here is the updated plunker:
https://plnkr.co/edit/HsECR0HEcIaPNbK6A7ZZ?p=preview

Ember model finding records

I'm having trouble getting Ember's queryRecord to work properly. I'm trying to grab a site config from the server.
//app/routes/application.js
model: function(){
return this.get('store').queryRecord('config',{}).then(function(config) {
console.log(config.get('appname'));
});
}
//app/adapters/config.js
import DS from "ember-data";
import ENV from './../config/environment';
export default DS.Adapter.extend({
queryRecord(modelName, query) {
return Ember.$.getJSON( ENV.APP.apiFull + 'config' );
}
});
//app/serializers/applications.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
keyForAttribute: function(attr, method) {
return Ember.String.underscore(attr).toUpperCase();
}
});
//JSON returning from server AJAX call
{"config":{"id":1,"environment": "development", "appname":"Sample App Name"}}
The console.log statement in //app/routes/application is returning undefined. This all seems to match up with the Ember documentation for version 2.9. What am I doing incorrectly?
#Lux, thanks for pointing me towards the serializer. That led to me looking at the model member names and I found that I had underscored them in the model, but not in the JSON coming from the server.

Angular 2: How do I get params of a route from outside of a router-outlet

Similar question to Angular2 Get router params outside of router-outlet but targeting the release version of Angular 2 (so version 3.0.0 of the router). I have an app with a list of contacts and a router outlet to either display or edit the selected contact. I want to make sure the proper contact is selected at any point (including on page load), so I would like to be able to read the "id" param from the route whenever the route is changed.
I can get my hands on routing events by subscribing to the router's events property, but the Event object just gives me access to the raw url, not a parsed version of it. I can parse that using the router's parseUrl method, but the format of this isn't particularly helpful and would be rather brittle, so I'd rather not use it. I've also looked all though the router's routerState property in the routing events, but params is always an empty object in the snapshot.
Is there an actual straight forward way to do this that I've just missed? Would I have to wrap the contact list in a router-outlet that never changes to get this to work, or something like that?
If any body was looking for the latest solution of this issue (angular 8) I stumbled upon this article which worked very well for me.
https://medium.com/#eng.ohadb/how-to-get-route-path-parameters-in-an-angular-service-1965afe1470e
Obviously you can do the same implementation straight in a component outside the router outlet and it should still work.
export class MyParamsAwareService {
constructor(private router: Router) {
this.router.events
.pipe(
filter(e => (e instanceof ActivationEnd) && (Object.keys(e.snapshot.params).length > 0)),
map(e => e instanceof ActivationEnd ? e.snapshot.params : {})
)
.subscribe(params => {
console.log(params);
// Do whatever you want here!!!!
});
}
In the hope to spare the same struggle I went through.
I've been struggling with this issue for the whole day, but I think I finally figured out a way on how to do this by listening to one of the router event in particular. Be prepared, it's a little bit tricky (ugly ?), but as of today it's working, at least with the latest version of Angular (4.x) and Angular Router (4.x). This piece of code might not be working in the future if they change something.
Basically, I found a way to get the path of the route, and then to rebuild a custom parameters map by myself.
So here it is:
import { Component, OnInit } from '#angular/core';
import { Router, RoutesRecognized } from '#angular/router';
#Component({
selector: 'outside-router-outlet',
templateUrl: './outside-router-outlet.component.html',
styleUrls: ['./outside-router-outlet.component.css']
})
export class OutSideRouterOutletComponent implements OnInit {
path: string;
routeParams: any = {};
constructor(private router: Router) { }
ngOnInit() {
this.router.events.subscribe(routerEvent => {
if (routerEvent instanceof RoutesRecognized) {
this.path = routerEvent.state.root['_routerState']['_root'].children[0].value['_routeConfig'].path;
this.buildRouteParams(routerEvent);
}
});
}
buildRouteParams(routesRecognized: RoutesRecognized) {
let paramsKey = {};
let splittedPath = this.path.split('/');
splittedPath.forEach((value: string, idx: number, arr: Array<string>) => {
// Checking if the chunk is starting with ':', if yes, we suppose it's a parameter
if (value.indexOf(':') === 0) {
// Attributing each parameters at the index where they were found in the path
paramsKey[idx] = value;
}
});
this.routeParams = {};
let splittedUrl = routesRecognized.url.split('/');
/**
* Removing empty chunks from the url,
* because we're splitting the string with '/', and the url starts with a '/')
*/
splittedUrl = splittedUrl.filter(n => n !== "");
for (let idx in paramsKey) {
this.routeParams[paramsKey[idx]] = splittedUrl[idx];
}
// So here you now have an object with your parameters and their values
console.log(this.routeParams);
}
}

Global variables Ionic 2

I'm having some difficulties with Ionic 2 and setting up global variables. The structure of my app is as follows:
Main app
|
|--- Page1 (Info)
|--- Page2 (Map)
|--- Page3 (List)
|
|--- ItemTabsPage
|
|---tab1
|---tab2
|---tab3
My intention is to show a list in Page3, and once one item is selected, to show additional information in tabs.
I send the information from Page 3 to the page with the tabs using:
itemTapped(event, item) {
this.nav.push(ItemTabsPage, {
item: item
});
}
The problem is that I can't do the same to send the info to the child tabs. I would like to show different information depending on which item is selected. I have tried defining an injectable globalVars.js to store the value in a variable:
import {Injectable} from 'angular2/core';
#Injectable()
export class GlobalVars {
constructor(myGlobalVar) {
this.myGlobalVar = "";
}
setMyGlobalVar(value) {
this.myGlobalVar = value;
}
getMyGlobalVar() {
return this.myGlobalVar;
}
}
and then updating the code of itemTapped in the list as follows:
itemTapped(event, item) {
this.nav.push(ItemTabsPage, {
item: item
});
this.globalVars.setMyGlobalVar(item);
}
However, I always get the same error:
Uncaught EXCEPTION: Error during evaluation of "click"
ORIGINAL EXCEPTION: TypeError: Cannot read property 'setMyGlobalVar' of undefined
The code for page3 is:
import {Page, NavController, NavParams} from 'ionic-angular';
import {ItemService} from '../services/ItemService';
import {ItemTabsPage} from '../item/item-tabs/item-tabs';
import {GlobalVars, setMyGlobalVar} from '../../providers/globalVars';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map';
#Page({
templateUrl: 'build/pages/item-list/item-list.html',
providers: [ItemService]
})
export class ItemListPage {
static get parameters() {
return [[NavController], [NavParams], [Http]];
}
constructor(nav, navParams, http, globalVars) {
this.nav = nav;
// If we navigated to this page, we will have an item available as a nav param
this.selectedItem = navParams.get('item');
this.http = http;
//this.items = null;
this.globalVars = globalVars;
this.http.get('https://website-serving-the-info.com/items.json').map(res => res.json()).subscribe(data => {
this.items = data.items;
},
err => {
console.log("Oops!");
});
}
itemTapped(event, item) {
this.nav.push(ItemTabsPage, {
item: item
});
this.globalVars.setMyGlobalVar(item);
}
}
Anyone have any suggestion? My Ionic installation is:
Cordova CLI: 6.1.1
Gulp version: CLI version 3.9.1
Gulp local: Local version 3.9.1
Ionic Framework Version: 2.0.0-beta.4
Ionic CLI Version: 2.0.0-beta.25
Ionic App Lib Version: 2.0.0-beta.15
OS: Distributor ID: LinuxMint Description: Linux Mint 17.3 Rosa
Node Version: v5.11.0
The easiest way I use is to create a file app/global.ts
export var global = {
myvar : 'myvar 01',
myfunction : function(msg) {
alert(msg);
}
};
Then import and use freely in other classes:
import {global} from "../../global";
constructor() {
global.myfunction('test');
}
and if you want to use this global to component HTML page as below
export class HomePage {
Global: any = Global;
now it is available in HTML as below
<div [style.display]="Global.splash ? 'flex': 'none'" >
You're on the right track. And some of the other answers will work, but the Ionic team is recommending you not use globals via a globals file. Instead, they recommend the use of Providers (as you're attempting to do).
You're provider is missing the actual variable declaration.
#Injectable()
export class GlobalVars {
myGlobalVar: string = '' // this is the line you're missing
constructor(myGlobalVar) {
this.myGlobalVar = "";
}
}
You should also note that you are not exporting the function setMyGlobalVar(). You are exporting the class GlobalVars which contains the function setMyGlobalVar().
I believe if you make those changes it should work.
edit
I'd also be careful of this line this.globalVars = globalVars; in your Page3. This will cause a rewrite of your globalVars each time Page3 is created.
I have exactly the same scenario, and would like to share my approach.
my understanding is that, in ionic2, the injection is implemented as instance. which means each time you enter a page, a new instance of the injection is created.
so direct access to a static value does not fit here; you have to somehow bridge the gap.
my approach goes as this:
you still defined a static value in your service provider, yet you define instance "getter", and "setter" for that value.
in your page implementation, you inject the service as a parameter of the constructor.
in the constructor, you have to "new" an instance of the service; and call the "getter", and "setter". see my code snippets below:
export class TransSender {
static _count:number = 0;
static _pushed:number = 0;
...
public static setter(count:number, pushed:number,...) {
TransSender._count = count;
TransSender._pushed = pushed;
}
public get count(){
return TransSender._count;
}
public get pushed(){
return TransSender._pushed;
}
...
}
I actually provide a static collective setter for the service to get value from backend in a static way.
my page implementation runs likes this
import {TransSender} ...;
#Component({
templateUrl: 'build/pages/basics/basics.html',
providers: [TransSender]
})
export class Page {
...
constructor(tSender: TransSender,...) {
...
tSender = new TransSender();
TransSender.setter(1,2,3,4,5,6,7,8);
console.log(tSender.count);
}
}
in your display (html), your will refer to tSender rather than TransSender
this might look a bit stupid. yet I can not find any other solution.
with the release of ionic2 Beta9, bootstrap was re-introduced into the frame. so I am exploring new possibilities
cheers
In your class ItemListPage, try this static parameters method before your constructor:
static get parameters() {
return [[NavController], [NavParams], [Http], [GlobalVars]];
}
I am thinking that you are setting your globalVars variable in the constructor to 'undefined' and therefore you cannot call a function on something that is undefined.
You seem to inject the GlobalVars provider incorrectly in ItemLisyPage.

Ember js use handlebars helper inside a controller?

I have a helper method that maps a number to a text -
Ember.Handlebars.helper('getStatusText', function (value, options) {
switch(value) {
case 1: return "Fresh";
break;
case 2: return "Callback";
break;
default: return "Unable to get Status";
}
});
I am able to use the helper in the view by using {{getStatusText 1}}
But how do I use the helper in an action inside an ObjectController ?
Test.DealController = Ember.ObjectController.extend({
selectedStatusType: null,
statusList: ["Fresh","Callback"],
actions: {
updateStatus: function(deal) {
// How do I call the handlebars helper here ?
console.log({{getStatusText 1}});
}
},
});
this obviously does not work.
What are the other ways ??
For better understanding, here is the jsbin
With ember-cli it can be done like this:
// helpers/foo.js
export function foo(params) {
return params;
}
export default Ember.Helper.helper(foo);
Helper foo exports a function (containing the helper logic) and the function wrapped in an Ember helper (for use in a template).
// helpers/bar.js
import { foo } from '<project>/helpers/foo';
export function bar(params) {
return foo(params);
}
export default Ember.Helper.helper(bar);
Helper bar imports the helper function from foo and uses it in it's own template helper.
Pull the logic out of the helper, making it available to be called both by the helper, and by non handlebars helper items alike. Parsing it into handlebars template and evaluating it is over complicating it.
Where you put it is up to you, you could namespace it to your app, or just create it as a function that lives with your helper.
function getStatusText(value){
switch(value) {
case 1: return "Fresh";
break;
case 2: return "Callback";
break;
default: return "Unable to get Status";
}
}
Ember.Handlebars.helper('getStatusText', function (value, options) {
return getStatusText(value);
});
http://emberjs.jsbin.com/cenep/1/edit
Most helpers are simple. In this case, exporting a vanilla function is the way to go. Pass the function exactly the data it needs.
Ember also has class-based helpers for a more complex use case. They can leverage other container dependencies. You can still have a class-based helper's compute method call your exported vanilla function.
However, the parameter list to the function could get unwieldy for other callers.
import Helper from 'ember-helper';
import service from 'ember-service/inject';
export function vanillaFunction(imageService, userService, foo, bar, baz, dependency3, options) {
...
}
export default Helper.extend({
imageService: service('image'),
userService: service('user'),
compute(positionalParams, hashParams) {
return vanillaFunction(this.get('imageService'), this.get('userService'), positionalParams[0], positionalParams[1], ...);
},
});
To benefit from container lookups, rather than call the vanilla function, you can manually instantiate such a helper and call compute yourself. This is rare. But it benefits from a small interface, uniform with how you'd call it in the template. The helper is normally instantiated by HTMLBars for use within a template, which has special context and observation rules. So there's a little hoop jumping to use it inside your e.g. controller.
import Controller from 'ember-controller';
import getOwner from 'ember-owner/get';
import setOwner from 'ember-owner/set';
export default Controller.extend({
someMethod() {
const owner = getOwner(this);
const helperFactory = owner.resolveRegistration('helper:my-helper');
const helperInstance = helperFactory.create();
setOwner(helperInstance, owner); // I'm not sure why the factory doesn't do this for you
return helperInstance.compute([1]); // "Fresh"
},
});

Categories