I have a single Ember app that needs to use different data depending on the domain that it's running on. For example, on domain1.com the site title might be "Domain 1 Website", whereas for domain2.org the site title could be "Cool Site!"
I need to be able to use the data in routes, controllers and templates.
My initializer so far:
import { request } from 'ic-ajax';
export function initialize(container, application) {
var domain = document.location.host;
return request('http://api/sites?domain=' + domain, {} ).then(function(response) {
// make the response available to routes, controllers and templates
});
}
export default {
name: 'site-data',
initialize: initialize
};
If you take a look at the docs:
http://guides.emberjs.com/v1.10.0/understanding-ember/dependency-injection-and-service-lookup/#toc_dependency-injection-with-code-register-inject-code
The second example
Ember.Application.initializer({
name: 'logger',
initialize: function(container, application) {
var logger = {
log: function(m) {
console.log(m);
}
};
application.register('logger:main', logger, { instantiate: false });
application.inject('route', 'logger', 'logger:main');
}
});
and the following explanation shows you how you can use .inject to make one of your variables, services or functions available to whatever objects you need within your application.
Related
I am using gulp to run and build to run my application. I am getting file contents using $http service in my index.js file and then setting value of a variable like
window.variablex = "http://localhost:8080/appname".
here is how I am doing it (in index.js)
(function ()
{
'use strict';
angular
.module('main')
.controller('IndexController', IndexController);
function IndexController($http){
$http.get('conf/conf.json').success(function(data){
window.variable = data.urlValue;
}).error(function(error){
console.log(error);
});
}
});
And I've created a factory to call the rest APIs of my backend application like
(function(){
'use strict';
angular
.module('main')
.factory('testService',['$resource',testService]);
function agentService($resource){
var agents = $resource('../controller/',{id:'#id'},
{
getList:{
method:'GET',
url:window.variable+"/controller/index/",
isArray:false
}
});
Now, I except a rest call to made like
http://localhost:8080/appname/controller
But it always sends a call like http://undefined/appname/controller which is not correct.
I can get the new set value anywhere else, but this value is not being set in resource service objects somehow.
I am definitely missing something.
Any help would be much appreciated
As you are using Gulp, I advise you to use gulp-ng-config
For example, you have your config.json:
{
"local": {
"EnvironmentConfig": {
"api": "http://localhost/"
}
},
"production": {
"EnvironmentConfig": {
"api": "https://api.production.com/"
}
}
}
Then, the usage in gulpfile is:
gulp.task('config', function () {
gulp.src('config.json')
.pipe(gulpNgConfig('main.config', {
environment: 'production'
}))
.pipe(gulp.dest('.'))
});
You will have this output:
angular.module('myApp.config', [])
.constant('EnvironmentConfig', {"api": "https://api.production.com/"});
And then, you have to add that module in your app.js
angular.module('main', [ 'main.config' ]);
To use that variable you have to inject in your provider:
angular
.module('main')
.factory('testService', ['$resource', 'EnvironmentConfig', testService]);
function agentService($resource, EnvironmentConfig) {
var agents = $resource('../controller/', {id: '#id'},
{
getList: {
method: 'GET',
url: EnvironmentConfig + "/controller/index/",
isArray: false
}
});
}
#Kenji Mukai's answer did work but I may have to change configuration at run time and there it fails. This is how I achieved it (in case anyone having an issue setting variables before application gets boostrap)
These are the sets that I followed
Remove ng-app="appName" from your html file as this is what causing problem. Angular hits this tag and bootstraps your application before anything else. hence application is bootstratped before loading data from server-side (in my case)
Added the following in my main module
var injector = angular.injector(["ng"]);
var http = injector.get("$http");
return http.get("conf/conf.json").then(function(response){
window.appBaseUrl = response.data.gatewayUrl
}).then(function bootstrapApplication() {
angular.element(document).ready(function() {
angular.bootstrap(document, ["yourModuleName"]);
});
});
This will load/set new values everytime you refresh your page. You can change conf.json file even at runtime and refreshing the page will take care of updating the values.
I am working on a site where I have to search in the DB for string that come after the / on the root domain. I can't find anything about it in the documentation.
I am trying to make it work with Iron Router but any other suggestion would work out.
Thanks for the help!
Edit: Basically I just want to pass anything that comes after domain.com/ to a variable.
Here's something i've been doing so maybe it'll lead you down the right path
Route sends URL params to ownedGroupList template
Router.route('/users/:_id/groups', {
name: 'owned.group.list',
template: 'ownedGroupList',
data: function() {
return {params: this.params};
}
});
Template ownedGroupList can access params object using this.data in onCreated, onRendered, and onDestroyed template event handlers
Template.ownedGroupList.onCreated(function(){
this.subscribe("owned-groups", this.data.params._id );
});
Template ownedGroupList can access params through this variable in helper methods
Template.ownedGroupList.helpers({
groups: function() {
return Groups.find({owner: this.params._id });
}
});
Template ownedGroupList can access params through template.data variable in event handlers
Template.ownedGroupList.events({
'click .a-button': function(event, template) {
var group = Groups.findOne({owner: template.data.params._id });
// do something with group
}
});
Here's a simple route that should do the trick
Router.route('/:keyword', {
name: 'keyword',
template: 'keywordTemplate',
data: function() {
return this.params.keyword;
}
});
This will pass the keyword as the data context to your template and then you can do whatever you want with it. Alternatively you can perform the search straight in the router (especially if you're passing the keyword to a subscription so that the search runs on the server). For example:
Router.route('/:keyword', {
name: 'keyword',
template: 'keywordTemplate',
waitOn: function(){
return Meteor.subscribe('keywordSearch',keyword);
},
data: function() {
return MyCollection.find();
}
});
This second pattern will send your keyword to a subscription named keywordSearch that will execute on the server. When that subscription is ready, the route's data function will run and the data context passed to your keywordTemplate will be whatever documents and fields have been made available in MyCollection.
I'm giving Vue.js a try and so far I'm loving it because it's much simpler than angular. I'm currently using vue-router and vue-resource in my single page app, which connects to an API on the back end. I think I've got things mostly working with a the primary app.js, which loads vue-router and vue-resource, and several separate components for each route.
Here's my question: How do I use props to pass global data to the child components when the data is fetched using an asynchronous AJAX call? For example, the list of users can be used in just about any child component, so I would like the primary app.js to fetch the list of users and then allow each child component to have access to that list of users. The reason I would like to have the app.js fetch the list of users is so I only have to make one AJAX call for the entire app. Is there something else I should be considering?
When I use the props in the child components right now, I only get the empty array that the users variable was initialized as, not the data that gets fetched after the AJAX call. Here is some sample code:
Simplified App.js
var Vue = require('vue');
var VueRouter = require('vue-router')
Vue.use(VueRouter);
var router = new VueRouter({
// Options
});
router.map({
'*': {
component: {
template: '<p>Not found!</p>'
}
},
'/' : require('./components/dashboard.js'),
});
Vue.use(require('vue-resource'));
var App = Vue.extend({
ready: function() {
this.fetchUsers();
},
data: function() {
return {
users: [],
};
},
methods: {
fetchUsers: function() {
this.$http.get('/api/v1/users/list', function(data, status, response) {
this.users = data;
}).error(function (data, status, request) {
// handle error
});
}
}
});
router.start(App, '#app')
Simplified app.html
<div id="app" v-cloak>
<router-view users = "{{ users }}">
</router-view>
</div>
Simplified dashboard.js
module.exports = {
component: {
ready: function() {
console.log(this.users);
},
props: ['users'],
},
};
When dashboard.js gets run, it prints an empty array to the console because that's what app.js initializes the users variable as. How can I allow dashboard.js to have access to the users variable from app.js? Thanks in advance for your help!
p.s. I don't want to use the inherit: true option because I don't want ALL the app.js variables to be made available in the child components.
I believe this is actually working and you are being misled by the asynchronous behavior of $http. Because your $http call does not complete immediately, your console.log is executing before the $http call is complete.
Try putting a watch on the component against users and put a console.log in that handler.
Like this:
module.exports = {
component: {
ready: function() {
console.log(this.users);
},
props: ['users'],
watch: {
users: {
handler: function (newValue, oldValue) {
console.log("users is now", this.users);
},
deep: true
}
}
}
};
In the new version of Vue 1.0.0+ you can simply do the following, users inside your component is automatically updated:
<div id="app" v-cloak>
<router-view :users="users"></router-view>
</div>
i am using Ember CLI + Ember Data + Simple Auth. The authenticator is working fine. But when im am doing a Rest Call with Ember Data Rest Adapter this.store.findAll("user"); the authorize function in my custom authorizer don't gets called.
The Rest API Endpoint is on an other domain, so i added the url to the crossOriginWhitelist in my environment.js.
environment.js:
module.exports = function(environment) {
var ENV = {
// some configuration
};
ENV['simple-auth'] = {
crossOriginWhitelist: ['http://api.xxxx.com'],
authorizer: 'authorizer:xxxx',
routeAfterAuthentication: 'dashboard',
};
return ENV;
};
authorizer
import Ember from 'ember';
import Base from 'simple-auth/authorizers/base';
var XXXXAuthorizer = Base.extend({
authorize: function(jqXHR, requestOptions) {
// Some Code, gets not called, damn it :(
}
});
export default {
name: 'authorization',
before: 'simple-auth',
initialize: function(container) {
container.register('authorizer:xxxx', XXXXAuthorizer);
}
};
index.html
....
<script>
window.XXXXWebclientENV = {{ENV}};
window.ENV = window.MyAppENV;
window.EmberENV = window.XXXXWebclientENV.EmberENV;
</script>
<script>
window.XXXXWebclient = require('xxxx-webclient/app')['default'].create(XXXXWebclientENV.APP);
</script>
....
Thanks for help :)
I had a similar problem. For me it was the crossOriginWhitelist config.
I set it like this:
// config/environment.js
ENV['simple-auth'] = {
crossOriginWhitelist: ['*'] // <-- Make sure it's an array, not a string
};
to see if I could get it working (I could), then I could narrow it down to figure out exactly what URL I should use to enforce the restriction (port number and hostname etc).
Don't leave it like that though!
You should actually figure out what URL works for the whitelist, and use that.
I am facing the same issue. I have same setup but the authorize function is not being called. May be you can try by adding the port number in your crossOriginWhiteList url.
I am adding window.ENV = window.MyAppENV line in new initializer which runs before simple-auth. You have added that in index file and may be that is the reason why simple-auth is not able to read your configuration.
Does the other configuration routeAfterAuthentication: 'dashboard', works properly? If not then this might be the reason. Try adding new initializer like
export default {
name: 'simple-auth-config',
before: 'simple-auth',
initialize: function() {
window.ENV = window.MyAppNameENV;
}
};
So,
I'm in the middle of implementing a plugin api for my application, and the plugins can have their own models, imagine this.
SimplePlugin = {
pluginName: 'simple',
pluginConfig: {},
SimpleModel: {
attributes: {
name: 'string'
}
}
}
So I need to be able to create the "one-time" model with a function whenever it's needed, it needs to have exactly the same functionality as other models so you automatically get the urls like /simplePlugin/:id for find ..etc
Thanks
What are you trying to do is not easy and a bit messy with Sails in the current state of the project. I'm referring to the v0.10 version. What you'll have to do is
Inject the model definition found in SimplePlugin.SimpleModel into sails.models
Inject a dummy controller for the model with _config: { rest: true }
Please note that the code examples I posted are taken from a custom Sails hook I am working on and assume access to sails and the code examples to be executed during the loadHooks phase of Sails initialization / before the MiddlewareRegistry phase (compare: lib/app/load.js).
1. Inject model definition
Following the hints in the orm hook in Sails v0.10 you have to:
Get the models and adapters defined in api, merge your new model into the dictionary
Normalize the model definitions via sails.hooks.orm.normalizeModelDef
Load the normalized model definitions into Waterline
Unload exisisting adapter connections via teardown
Reinitialize Waterline
Expose the initialized Waterline collections to sails and the global scope via sails.hooks.orm.prepareModels (previously: sails.hooks.orm.exposeModels, changed with: 8d96895662)
Because you have to reinitialize Waterline and reload all model definitions I'd recommend to collect all model definitions to inject and pass them to the inject function once. The example code below reflects this.
...
function injectPluginModels(pluginModels, cb) {
// copy sails/lib/hooks/orm/loadUserModules to make it accessible here
var loadUserModelsAndAdapters = require('./loadUserModules')(sails);
async.auto({
// 1. load api/models, api/adapters
_loadModules: loadUserModelsAndAdapters,
// 2. Merge additional models, 3. normalize model definitions
modelDefs: ['_loadModules', function(next){
_.each(additionModels, function(aditionModel) {
_.merge(sails.models, additionalModel);
});
_.each(sails.models, sails.hooks.orm.normalizeModelDef);
next(null, sails.models);
}],
// 4. Load models into waterline, 5. tear down connections, 6. reinitialize waterline
instantiatedCollections: ['modelDefs', function(next, stack){
var modelDefs = stack.modelDefs;
var waterline = new Waterline();
_.each(modelDefs, function(modelDef, modelID){
waterline.loadCollection(Waterline.Collection.extend(modelDef));
});
var connections = {};
_.each(sails.adapters, function(adapter, adapterKey) {
_.each(sails.config.connections, function(connection, connectionKey) {
if (adapterKey !== connection.adapter) return;
connections[connectionKey] = connection;
});
});
var toTearDown = [];
_.each(connections, function(connection, connectionKey) {
toTearDown.push({ adapter: connection.adapter, connection: connectionKey });
});
async.each(toTearDown, function(tear, callback) {
sails.adapters[tear.adapter].teardown(tear.connection, callback);
}, function(){
waterline.initialize({
adapters: sails.adapters,
connections: connections
}, next)
});
}],
// 7. Expose initialized models to global scope and sails
_prepareModels: ['instantiatedCollections', sails.hooks.orm.prepareModels]
}, cb);
};
...
Would allow you to:
// Read your plugins
...
var pluginModels = // Get all the plugin models
injectPluginModels(pluginModels, function(){
// Plugin models now available via global[pluginModel.globalId] and sails.models[pluginModel.identity]
});
2. Inject controller
For each model that should be exposed via blueprint methods you have to:
Create a controller definition with matching identity and enabled blueprints
Save controller to sails.controllers[controllerId]
Save controller to sails.hooks.controllers.middleware[controllerId]
The Sails MiddlewareRegistry will automatically pick up the controllers found in these objects.
function mountBlueprintsForModels(pluginModels) {
_.each(pluginModels, function(pluginModel){
var controller = _.cloneDeep(pluginModel);
controller._config = { rest: true };
var controllerId = pluginModel.identity;
if (!_.isObject(sails.controllers[controllerId])) {
sails.controllers[controllerId] = controller;
}
if (!_.isObject(sails.hooks.controllers.middleware[controllerId])) {
sails.hooks.controllers.middleware[controllerId] = controller;
}
});
}
3. In action
// E.g. in /api/hooks/plugins/index.js
/*
* Module dependencies
*/
var async = require('async'),
_ = require('lodash'),
waterline = require('waterline');
module.exports = function(sails) {
// injectPluginModels and mountBlueprintsForModels defined here
...
return {
initialize: function(cb) {
sails.after('hook:orm:loaded', function() {
yourNiftyPluginLoader(function(err, plugins) {
// assuming plugin.models holds array of models for this plugin
// customize for your use case
var pluginModels = _.pluck(plugins, 'models');
injectPluginModels(pluginModels, cb);
mountBlueprintsForModels(pluginModels);
});
});
}
}
}
EDIT: not working completely since collections are assigned to connections at initialization.
Seems that there is a better solution, with 3 lines of code and without disconnecting/reconnecting databases. I just studied the source code of Waterline (see https://github.com/balderdashy/waterline/blob/master/lib/waterline.js#L109). It's possible to do something like:
var Waterline = require('waterline');
// Other dependencies
var Schema = require('waterline-schema');
var CollectionLoader = require('waterline/lib/waterline/collection/loader');
var orm = new Waterline();
var config = {
// Setup Adapters
// Creates named adapters that have have been required
adapters: {
'default': 'mongo',
mongo: require('sails-mongo')
},
// Build Connections Config
// Setup connections using the named adapter configs
connections: {
'default': {
adapter: 'mongo',
url: 'mongodb://localhost:27017/sausage'
}
}
};
orm.initialize(config, function(err, data) {
if (err) {
throw err;
}
// ORM initialized, let's add another model dynamically
var User = Waterline.Collection.extend({
identity: 'user',
connection: 'default',
attributes: {
first_name: 'string',
last_name: 'string'
}
});
orm.loadCollection(User);
var defaults = config.defaults || {};
// This is where the magic happens
var loader = new CollectionLoader(User, orm.connections, defaults);
var collection = loader.initialize(orm);
orm.collections[collection.identity.toLowerCase()] = collection;
// Done! You can now use orm.collections.user :-D
});
In v0.12 sails.hooks.orm.normalizeModelDef doesn't exists anymore. Also sails/lib/hooks/orm/loadUserModules went to the sails-hook-orm npm module and is not longer part of sails.
Try this:
"Load models, controllers, services, policies and config from specified directories and inject them into the main Sails app."
https://github.com/leeroybrun/sails-util-mvcsloader
surprised sails doesn't support this in 2018: I have continued the above package with a fork ( #eyn answer) with updates that work for sails v1.x.x.
https://github.com/emahuni/sails-util-micro-apps
I changed it to that coz I am changing a lot of thing in that package. Instead of loading just models and controllers i want to it to load whole apps, mini-apps for a micro service architecture in sails. This is such that you can make mini-apps that can be joined together to form one large app out of reusable apis code.