Best practise to load content when Ember application boots - javascript

I need to load contents coming from my API when the application boots, then inject what I get into all routes and controllers to be able to access them whenever I want.
I was wondering where is the best place to do that in Ember?
In an initializer? I've heard that it's not a good practise to use the store from there...
In the application route? Then how can I access it from all routes and controllers? Using this.modelFor('application')? Is that a good practise?
Thanks.

The best place would be in the ApplicationRoute, you can do this in the beforeModel or afterModel/setupController hook as you like. Here's a beforeModel example:
ApplicationController = Ember.ObjectController.extend({
beforeModel:function() {
var self = this;
var rsvp = Ember.RSVP.hash({
fruits: self.store.find('fruit'),
candies: self.store.find('candy'),
meats: self.store.find('meats')
}};
rsvp.then(function(models) {
self.controllerFor('fruits').set('model',models.fruits);
self.controllerFor('candies').set('model',models.candies);
self.controllerFor('meats').set('model',models.meats);
});
}
});
The rsvp fetches all of the models together and waits for them to precede before continuing which happens on the then. We then assign all the found models to the model property on their matching controller.
To do this in the afterModel hook, it would look different.

Related

Global Variable in Javascript for Laravel Routes - Is this a good idea?

I've created some code using a View Composer where I am passing my Route Collection through to the front end on all views, so I can access all of my laravel routes in Vuejs via the route named associated with them.
For example, to upload an image using a vue component, instead of passing my upload route into the Vue Component, it is listed as a part of a global variable:
var uploadRoute = _.find(globalRoutes, function(route) { return route.name == 'route-name.image.upload' });
$.post(uploadRoute, data) ... etc
My question is...is this sensible? I'm publically publishing my entire app's routes.
Thanks
I think your hunch about exposing your entire apps routes is legit. IMO you should explicitly pick out the routes that you need. So in thise case, you should only expose route-name.image.upload. You could create a tiny helper function to look up routes and output them along with the URL as JSON.
function json_routes(array $routes)
{
$return = [];
foreach($routes as $route)
{
$return[$route] = route($route);
}
return new \Illuminate\Support\HtmlString(json_encode($return));
}
And the, in your main view:
var routes = {{ json_routes(["route-name.image.upload"]) }};
Getting a route is simple:
routes['route-name.image.upload'];
This is the most basic exaple I can think of. You can optimize it quite a bit. Just some ideas:
Place the routes in a central place, fx. a config element: json_routes(config('app.json_routes'))
Build a command that generates a static .json file so that you don't iterate through the routes on each page load. Remember to re-generate when you add more routes.
Create a function instead of an object to get the route. That allows you to build in logic and gives a more Laravel-like feel in your js: function route(path){ return window.routes.hasOwnProperty(path) ? window.routes[path] : null ;}
(Advanced) Re-write Laravels router logic and hook into the options array, allowing you to do something like Route::get('dashboard', '...', ['as'=>'dashboard', 'expose'=>true]);, then dynamically generate the before mentioned json-file on all routes with the expose option.

Ember.js: dependencies between two controllers failing

I am trying to access one of two models in a controller that uses needs on a sibling controller. My router looks like the following:
App.Router.map(function() {
this.route('login');
this.route('mlb.lineups', {path: 'tools/mlb/lineups'})
this.resource('mlb.lineups.site', { path: 'tools/mlb/lineups/site/:site_id' });
});
The mlb.lineups route definition looks like the following:
App.MlbLineupsRoute = Ember.Route.extend({
model: function() {
var self = this;
return Ember.RSVP.hash({
sites: self.store.find('site')
})
},
setupController: function(controller, models) {
controller.set('model', models.get('sites'));
},
afterModel: function(models) {
var site = models.sites.get('firstObject');
this.transitionTo('mlb.lineups.site', site);
}
});
The reason I am using Ember.RSVP.hash({}) here is I plan on adding another model to be retrieved after I retrieve the site model.
Now in my MlbLineupsSiteController I am trying to access the sites model with the following:
App.MlbLineupsSiteController = Ember.ArrayController.extend({
needs: "mlb.lineups",
sites: Ember.computed.alias("controllers.models.sites")
});
This is the error I'm getting in my Ember console: needs must not specify dependencies with periods in their names (mlb.lineups)
What's the best way to make the sites model from the MlbLineups controller available in my MlbLineupsSiteController?
Note:
#NicholasJohn16's answer isn't valid anymore. It always gives an error that controller couldn't be found. Generally you should also never use needs property and always use Ember.inject.controller if you have to make your controllers dependent on each other. I'd also recommend using services instead of dependencies between controllers. It's easier to maintain code which contains communication between controllers through services, than controller directly accessing other controller's properties. You might not always be aware of such access, and using services gives you another layer of security.
Solution:
Tested in Ember.js 1.10.0-beta.4. Use following code in Controller to reference nested controller in needs:
needs: ['classic/about']
Then you can access it later using:
const aboutController = this.get('controllers.classic/about');
const aboutProperty = aboutController.get('customProperty');
Works as expected. Basically you need to replace dots with slashes.
It should be:
needs:" MlbLineupsSite "
Basically, the name of the controller you want to include, minus the word controller.
Everything else you posted should work.

Ember.js - Setting a model and routing dynamically via API data

So I'm working on building a dynamic model for a project that reacts to data sent from an API. The api will return, among other things, what your location should be and this in turn becomes the url. So, eg:
{
location: 'xyz'
(...)
}
So currently my router will transition to the right route dynamically. But I still have to hardcode each route ( IndexRoute, LocationXYZRoute, LocationABCRoute, etc).
My goal is to create a single route that handles things dynamically. We'll call it App.LocationRoute and my routes would look something like:
App.Router.map(function() {
this.resource(':location', function() {
this.route(':subLocation')
}
}
Now, I have two architectural questions:
1) Whats a good way to handle this sort of dynamic routing? (I've read through the guide about dynamic routing using the ':post_id' type example, but I think I need a more holistic example to really grasp it.
2) The API sends back a whole host of other data as well. I want to add this to the route's model but I also have some other static models. Doing...
this.controllerFor(location).set('content', APIdata);
... works, but it does not set for routes currently using static models. I tried something like:
this.controllerFor(location).set('apiData', APIdata);
and...
this.controllerFor(location).set('model:apiData', APIdata);
... but neither worked.
Any suggestions?
1) Yes, you should use dynamic segment
this.resource('location', { path: '/location/:location_id' }, function() {
this.resource('sublocation', { path: '/sublocation/:location_id' });
});
2) Are you using ember-data? You could check sideloaded data. Anyway, you could read the json and set the payload of each entity for each specific route.
this.controllerFor('location').set('content', APIdata.location);
this.controllerFor('user').set('content', APIdata.user);
People could help you better, if you separate your questions and create a http://emberjs.jsbin.com/ with isolated each specific case?

how to model my data is angular js? Does something like Backbone.Model will fit?

I am using angular-js and I am looking for a way to model my data. I am inspired by Backbone and started to write something similar to Backbone model. In Backbone model there are get() and set() methods for accessing model's members.
What is the advantages of using get() and set() methods over using model.attributes.__?
If I don't need my angular models to trigger changes events, does it worth to create get() and set() methods that are similar to Backbone.Model's get() and set()?
EDIT
To be more clear, I am asking if there is advantage to make a set() and get() methods over direct access to the fields. If there is no any advantage, why does Backbone people decided to go that way?
You just need to create a service which would hold the data, you can think of services like instances of models or collections. For example, you have some domain class like Book, what you can do is to create a service 'BookListService' which would be responsible for CRUD and/or business logic.
Something like this
function Book(title, author){
this.title = title;
this.author = author;
this.isRead = false;
}
Book.prototype.markAsRead = function(){
this.isRead = true
}
angular.module('someModule', []).factory('BookListService', function($http){
var service {
books: [],
load: function(){
$http.get('/books').then(function(res){
angular.forEach(res.data, function(book){
service.books.push(new Book(book.title, book.author))
})
})
}
};
return service;
})
Angular's 'models' are any object in javascript.
If you check out the tutorials on angularjs.org,
http://jsfiddle.net/api/post/library/pure/
The data is defined in the Controller. So make an array, just do
$scope.todos = [
{text:'learn angular', done:true},
{text:'build an angular app', done:false}];
To get/set is just normal javascript:
get length: $scope.todos.length;
set to empty: $scope.todos = [];
In the view (html), just call the variable if its under the same Controller
<div ng-controller="TodoCtrl">
<span>{{todos.length}}</span>
</div>
I had some trouble with this at first because Angular seems overwhelming, so I was expecting a big answer for model implementation. The answer is simply to use JS objects. Angular has all of the tools you need to easily create a model yourself without the use of Backbone (which I have also read is not really the way to go with Angular). If you look into $emit, $broadcast, $on, you have your events for model updates. If you want to create instances of the objects rather than singletons, then have your factory return an instance. A good place to start I think is angular-app. Apologies if this isn't specific enough.
If you are looking for a package to use, try BreezeJS.

Using Bindings with the EmberJS Router?

I'm working with Ember.Router, and one thing I can't figure out is how to bind objects to controllers that the Router is instantiating.
For instance, here is a controller class (extended) that the Router will instantiate for a specific route ('page'), as well as a controller object (created), say to handle user administration tasks on a part of the application outside of the Router:
// controller used by Router to render the "page" route
App.PageController = Em.ObjectController.extend({
content: Em.Object.extend({
foo: 'bar'
})
});
// global controller for users
App.usersController = Em.ObjectController.extend({
content: Em.Object.extend({
fooBinding: App.PageController.foo
// the above will not work since Em.Router
// instantiates the page controller dynamically
})
});
So when the router loads it will instantiate App.PageController into App.router.pageController, but that's after App.usersController is already created. So how can App.usersController access data in a controller that the Router is managing?
Any ideas?
There are a few mistakes in your sample.
First, you should never directly setup a property with an Object value at declaration time: this value would be shared across all instances of the class. Here, it does not really matter, but it's a bad practice. In this case, the good way of setting up the PageController content is to bind it in router, at connectOutlet call, like that:
connectOutlets: function (router) {
var theContainerController = router.get('theContainerController'),
objectWithFooBar = Ember.Object.create({
foo: 'bar'
});
theContainerController.connectOutlet('page', objectWithFooBar);
}
Second mistake is the naming of usersController: it should be UsersController, as it is a class, which will be injected in the router as usersController during initialize call. It seems also quite strange to have users pluralized & ObjectController. Certainly should be singularized...
Last, and certainly what will be the most interesting regarding the question, once you will have preceding remarks applied, you will be able to setup the binding using:
App.UserController = Em.ObjectController.extend({
fooBinding: 'App.router.pageController.foo'
});
App.router can be setup before your call to App.initialize. It is definitively a bad coupling to have UserController using a global symbol to directly access to PageController, but it does the job in your case.
A definitely yet better solution would also be to bind UserController's content in a connectOutlet call.

Categories