I am trying to make sense of some code snippets in javascript. But getting way too confused, it seems to be using all the symbols in all possible ways.
import Ember from 'ember';
export default Ember.Route.extend({
metrics: inject.service(),
activate() {
this._trackPage();
},
_trackPage() {
run.scheduleOnce('afterRender', this, () => {
const page = document.location.href;
const title = this.routeName;
get(this, 'metrics').trackPage({ page, title });
});
}
});
q1: What are the keyword service, get, extend? What are they doing here?
q2: Why is activate defining _trackPage separately? Why not put the reschedule code in activate() itself?
Basically trying to understand: https://emberway.io/applying-the-adapter-pattern-for-analytics-in-ember-js-apps-29448cbcedf3
A service is a place to put code that can be used almost anywhere in the app. In order to make the code inside a service available to the script you're working on, you inject it.
get is a helper that is used to access things like the service or Ember objects. In more recent tutorials, you'll see syntax that looks more like this.get('metrics'). See the docs here.
You could put the _trackPackage code into the activate function if you wanted to. However, activate is a special function called a hook, and many developers like to keep their route hooks like activate as short and sweet as possible for stylistic reasons. Hooks are special functions that fire automatically on things like a user entering a route, rerenders, etc. There are many kinds of hooks like this throughout Ember.
Extend is Ember boilerplate that gets created for you when you use the Ember CLI to make new files. Basically, when you create a route, you're extending on Ember's default route configurations, attributes, and methods. So there's some base behavior (like the activate hook) that gets inherited.
I recommend that you read the Ember Guides sections on objects and services for more info.
Related
I have about 8 simple UI's for different applications, all of them make use of a shared components library I also produce. I recently added a feature to modify how querying is done in the low levels of a some utility functions used in all the saga of all my UIs.
I want each of my UIs to be able to include an optional configuration value to tweak this new querying logic in their configuration files. However, the method that needs the property is very low level, I'd prefer not to have to add an extra property to 5-6 methods to pass one value down to the method that needs it.
These shared components are used only for my UI, which all have a standard format. As such I don't need them to be fully generic, I have no complaint with hard coding their checking the standard location of my configuration file in my UIs. However, since the shared components are a separate NPM package I don't know how to reference the location of a configuration file in the applications calling the UI.
Is there any clean way to get the value from the configuration file other then just passing it through all the relevant methods?
It's simple. You can create configuration file where you'll export your logic. And when you need that config, import that and use. Here's an example:
configuration.js
export const test = (myvar) => {
return 'test: ' + myvar
}
component.js
import { test } from 'configuration.js'
test('pass') // test: passed
I'm pretty new to Vue and I'm trying to create my first app with it (I have worked through a whole tutorial though so it's not like I'm completely new to it). I come from a Java/Grails background so this new "frontend oriented webapps" is still pretty confusing to me. I'm using Vue 2 with webpack.
The problem I'm having is running methods when the app initializes which creates data in the App.vue component (which I'm assuming is the root component, is that correct?) and then accessing this data in child components. So specifically what I'm trying to do is on in the 'created' life cycle hook I want to check if the user is logged in or not and then I want to update my navbar accordingly (show a login button if not, else show the user's name, for example).
I haven't even quite figured out how exactly I'm gonna determine if the user is logged in yet coz so far I've only been trying to create dummy data in the App.vue component and then accessing it in child components. Everywhere that I've researched says that I should use an event bus but (I think) that's not gonna work for me (correct me if I'm wrong) coz the only examples I can find is all $emit functions being called on user events (like button clicks) and I want it to have the data globally accessible (and mutate-able) everywhere all the time.
So I'll try to show you some code of what I had in mind:
App.vue:
...stuff...
data() {
return {
authToken = '',
userdetails = {},
loggedIn = false
}
},
created: function() {
// check browser storage for authToken
if(authToken) {
// call rest service (through Vue Resource) to Grails backend that I set up
// beforehand and set this.userdetails to the data that gets returned
if(this.userdetails) {
this.loggedIn = true;
}
}
}
...stuff...
Home.vue:
<template>
<div class="home">
<nav-bar></nav-bar>
</div>
</template>
...stuff
NavBar.vue:
<template>
<div class="navBar">
<div v-if="loggedIn">Hi {{ userdetails.name }}</div>
<div v-else>Please log in before continuing.</div>
</div>
</template>
Please excuse if any of that code has any mistakes in it, it's just to show more or less what I'm trying to do and I made most of it up right now. So the main question: How do I go about getting the v-if="loggedIn" and {{ userdetails.name }} part to work (coz obviously the way it's set up now that won't work, right?). And then besides that, any general advice on "global variables" and data flow in Vue js will be appreciated coz I believe that my server-side app mentality might not work in front-end javascript apps.
To get Data from parent component you can use this.$parent.userdetails in the child component.
But a much better way is to use props like this
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-with-Props
Lite pattern, not for complex sharing tasks
You can access global app component everywhere (routes, components, nested components) with the predisposed field:
this.$root
Notes for overengineers: It is officially suggested as "lite" approach in vue guide: https://v2.vuejs.org/v2/guide/state-management.html.
Obviously, it is not a architectural patternized solution, but ok for just sharing a variable or two.
In these case this lean approach avoids developer to import a full sharing framework like vuex, heaving development, know-how requirement and page download, just for such a trivial sharing task.
If you dislike lite approaches, even when appropriated and officially suggested, maybe is not how to share the problem, but which framework to use.
For instance, Angular is probably best fitted for heavy engineered approach, and I not mean one is better than the other, I mean different instruments suite best for different task and approaches, I too use it when need to realize a heavier and more complex solution, in these case even with vuex I could not handle all the components interaction and realtime data sharing.
Following methods are not recommended by other developers or vue itself.
this.$root;
or
this.$parent.userdetails
In my opinion the best way to share any data across components is using vuex state management.
Here is the official documentation link: https://vuex.vuejs.org/#what-is-a-state-management-pattern
I'm quite new to AngularJS so please bear that in mind when reading this question...
I have some functions that I would like to make globally available to different modules within my website, plan is to have pages performing their own functions in a single page app style (so a user list / create / modify would be one page, and a product list / create / modify would be another). I would like to have some shared logic, say utility functions, and also user authorisation that can be shared between the different page modules.
This leads to my question.
Assuming I have all the account functions encapsulated within a service (app.factory('account, etc etc...') for example) and separated into it's own JS file, is it better to place it within it's own module and using dependency injection like so:
var accountMod = angular.module('accountModule', ['dependencies']);
accountMod.factory('account', ['dependencies', function (...) { }]);
Or just assume the name of the app variable will always be app and express it like so:
app.factory('account', ['dependencies', function (...) { }]);
Functionally both of these work, but I am trying to use best practices. The second option seems limited, I have never been keen on assuming variable are the same name throughout, for me the dependency injection method seems better but I have seen many examples of both styles out there!
Any help is much appreciated!
Really nice question. There are subtle things in this.
I think it would helpful to use following code, which is using module.
var accountMod = angular.module('accountModule', ['dependencies']);
accountMod.factory('account', ['dependencies', function (...) { }]);
However with help of angular provider and adding module level config we can mock object or service. Eventually this will increase the test ability of code.
If there are multiple services under accounting, then I would prefer to group them inside module.
These are my aspect of to look at it. Please add more if you found.
Thanks.
Just my 2 cents on your code examples.
The following approach is not recommended:
var accountMod = angular.module('accountModule', ['dependencies']);
accountMod.factory('account', ['dependencies', function (...) { }]);
A best practice is to only have 1 component per file, therefore no need to define a variable. Take a look at this: https://github.com/johnpapa/angular-styleguide#definitions-aka-setters
If you are just starting out with Angular, I recommend that you go through the rest of John Papa's Style Guide.
I love the structure that angular fullstack generator for yeoman has.
https://github.com/DaftMonk/generator-angular-fullstack
You can see how each module and component is separated and inside, de factory, services, directives, etc. and their associate test are split in one file per functionality.
This probably is overkilling for your propose, you can take only the angular idea.
In ember.js (I am using 1.1.2) How do I 'send/redirect' a user to a particular url programmatically (without forcefully changing window.location.href).
I have a hybrid application, where we run some legacy code and an Ember "app". In order to make the boundaries really explicit, we created a simply library we called the 'LegacyBridge'. It helps external code call into Ember without having to know too much about the internals. It's also a nice way to limit what external code can do with your Ember App, since there's a lot they could do and it's a good idea to keep limit the options.
Anyway, here's what you could do:
var transitionTo, getContainer;
var getContainer = function() {
return App.__container__;
};
var transitionTo = function(route) {
var router = Ordering.__container__.lookup('router:main');
router.transitionTo(route);
};
this.App.LegacyBridge = {
transitionTo: transitionTo
};
Then somewhere in the non-Ember code:
App.LegacyBridge.transitionTo('posts/1');
This is certainly hacky. In general I wouldn't lookup things directly from the container, but since it's well encapsulate and this is more of an exceptional use case I can live with it.
BTW, this is better than changing the URL, since everything will work even if your router changes the location strategy from history to hash or none, if the rootUrl changes or for browsers not supporting pushState (Ember now will fallback to hash)
Hope this helps.
Check out the guide on redirection.
Ember allows you to transition to a route via it's name or it's URL:
router.transitionTo('post', post)
router.transitionTo('/posts/1')
EDIT:
I'm not sure of a good way to attain a reference to the router from outside your Ember application other than the very discouraged App.container.lookup('router:main'). If you can still handle this UI interaction within your Ember app, it's as simple as adding an action (a method inside the actions hash) to your ApplicationRoute.
Manipulating window.location is probably the 'cleanest' way of triggering a transition from outside your Ember app.
I can't to get a good idea of how you support permalinks. This is perhaps simply because emberjs doesn't support permalinks.
I'm building a UI that allows users to choose certain reports, so in ember this is easy enough you just add a route 'reports' that takes in an id so the URL looks like this:
#/reports/10/
What I need to have is some extra parameters to also be present in that URL for example start and end dates and metric types e.g.
#/reports/10/metric/impressions/start/10-08-2013/end/11-08-2013
So those parameters need to be picked up if I paste them in the browser, AND importantly when the user changes those settings e.g. via a date picker the URL should update to reflect the new parameters.
Edit:
Here is a link to a jsbin with a solution based on the answer below. http://jsbin.com/ucanam/703
Just throwing my 2 cents into this topic. Please note that i am using this approach in production and it works fine. Actually there are 2 parts to this question.
1. How can i have multiple dynamic segments?
One approach is described by Mike Grasotti using nested resources. This approach works but i think this approach is a little bit cumbersome in this case.
Why do i think it is cumbersome?
Routes are a means to separate concerns in Ember. In this case i do not see separate concerns. It looks to me like you are trying to mirror the state of a form in your URL. I think it should be one route that is responsible for the concern "state of the form". Therefore i recommend to have a look at the following post, in which i describe how to implement multiple dynamic parameters per Route: Is resource nesting the only way to enable multiple dynamic segments?
2. How is it possible to trigger the serialize hook to update the URL, when you have changed just one parameter in your form?
The problem is that the serialize hook is only triggered, when the Route gets entered with a new model. I guess you have some logic in place, that deals with the event of changing the parameters start or end. I suppose that you do not re enter the Route in this case. So how do you trigger the serialize hook in this case again to update the URL? I am handling a event like this in my router and there i am using the following code:
var currentRouteName = this.controllerFor("application").get("currentPath");//the ApplicationController stores the name of the current Route
var url = App.Router.router.generate(currentRouteName);
App.Router.router.updateURL(url);
PS: You can have a look at my production app here. This app shows the best movies in cinemas in Germany. Even if you do not know german, you can click on one of the controls in the top area and see the URL getting updated. I guess this is pretty much the same you want?
I have wondered how to do this as well. The data has to go in the URI since we want to share links, but you don't want to confuse the application location with the application state.
The hash: #/reports/10 would be the minimal information required to tell the application where to go. All the other data which is independent of location should probably go in the search portion of the URI. I would suggest something like this:
#/reports?metrics=impressions&start=10-08-2013&end=11-08-2013
In theory you could parse the query string when you enter a route and then update your model accordingly. From my understanding the route's model() function is called when navigating to a route by changing the URL directly or clicking a link so that would be the place.
Unfortunately, in practice this didn't work as I expected. I'm not sure if it's just JSBin, but there is some weird behavior where the link with the extra application data doesn't actually navigate which is the whole point for a permalink. Notice that if you follow the directions in the JSBin the start and end dates are taken from the url instead of the default values. This concept could be extended to send extra requests for different model data using findQuery etc so almost any thing is possible.
http://jsbin.com/abadet/7
Anyways, it might give you some ideas.
There are a couple of ways to get this done in ember. If you need a lot of flexibility for misc parameters that might be passed to a report, check out ember-query which adds query-string support to ember applications.
Another option is to use nested resources. So for example:
App = Ember.Application.create({});
App.Router.map(function() {
this.resource('report', {path: '/reports/:report'}, function() {
this.resource('metric', {path: '/:metric'}, function() {
this.resource('start', {path: '/:start'}, function() {
this.route('end', {path: '/:end'});
});
});
});
});
App.StartEndRoute = Ember.Route.extend({
model: function(params, transition){
return transition.params
}
});
<script type="text/x-handlebars" data-template-name="start/end">
<pre>
Report ID: {{report}}
metric: {{metric}}
start: {{start}}
end: {{end}}
{{log this}}
</pre>
</script>
See this jsbin for working example