Not able to scuffle between states and sharing data using ui-router - angular-ui-router

I'm using like this
$stateProvider
.state('/', {
url: '/Home',
templateUrl: 'Home.html',
controller: 'ctrl'
})
.state('category', {
url: '/category',
templateUrl: 'category.html',
controller: 'ctrl'
})
.state('Item', {
url: '/Item',
templateUrl: 'Item.html',
controller: 'itm'
})
Currently i'm using $rootScope.category=[]; to display category.is there any other way to do this.
i'm trying to load category with ng-init in Home state and on category click i want to load items with category id.How could i do this.

Plunkr https://plnkr.co/edit/A8JXIWa6Jc4RxsGvucFa?p=preview
Best way to go about doing this sort of nested view is with child states and resolve blocks.
.state('app', {
url: '',
template: '<div ui-view></div>',
controller: 'AppCtrl',
abstract: true
})
The main state in my example, 'app' is an abstract state. Abstract states can have child states but cannot be accessed directly. If we prefix all of the states in our application with 'app.' they will be a child of the app state and will inherit the scope of the app state. Notice how I use $scope.goHome() in the category controller even though it is defined in the app controller.
.state('app.home', {
url: '/home/',
templateUrl: 'app.home.html',
controller: 'HomeCtrl',
resolve: {
Categories: function (CategoryService) {
return CategoryService.getAll();
}
}
})
.state('app.category', {
url: 'category/:categoryId/',
templateUrl: 'app.category.html',
controller: 'CategoryCtrl',
resolve: {
Category: function ($stateParams, CategoryService) {
return CategoryService.getById(+$stateParams.categoryId);
},
Items: function ($stateParams, ItemService) {
return ItemService.getByCategoryId(+$stateParams.categoryId);
}
}
});
These next two states in the example, 'app.home' and 'app.category' are the ones most relevant to your question. Notice how I have resolved the data we need to populate the view before entering the state. This functionality happens in the resolve block, and all variables that we resolve in the state can be accessed directly in the state controller (as well as all childrens' controllers) or in consecutive resolve statements.
For the sake of abstracting out reusable functionality I created an ItemService and CategoryService that I use in the resolve block. They are relatively simple and can be seen in the plunkr. The two important functions are CategoryService.getById and ItemService.getByCategoryId. These both take the same param categoryId for which I retrieve from the state's $stateParams which are passed in to the $state.go function as the second parameter as seen in the HomeCtrl controller. These parameters are be included in the url in the state.
.controller('AppCtrl', function ($scope, $state) {
$scope.goHome = function () {
$state.go('app.home');
}
})
.controller('HomeCtrl', function ($scope, $state, Categories) {
$scope.Categories = Categories;
$scope.goCategory = function (category) {
$state.go('app.category', {categoryId: category.id});
};
})
.controller('CategoryCtrl', function ($scope, Category, Items) {
$scope.Category = Category;
$scope.Items = Items;
});
The controllers here are also relatively simple. The resolved variables are injected into the controllers (and must be named the same). They are then assigned to the scope so that they can be accessed in your templates.
I went ahead and created models for both categories and items as an unnecessary, but useful abstraction. They are unnecessary in regards to state management.
Hopefully this example is enough to get you started with nested states in ui-router.

Related

Update Ember model live via action

I'm new to Ember.js and keep struggling on a simple task. My goal is to achieve live update of the page content after action is triggered. I'm quite lost in Ember logics regarding route-controller-model relationship in this case.
So my template.hbs is something like:
<h1>{{model.somedata}}</h1>
<button {{action 'getContent'}}>Get Content</button>
My controller accepts some params from user form and makes the AJAX call:
export default Ember.Controller.extend({
somedata: 'hello',
actions: {
getContent: function () {
var self = this;
Ember.$.ajax({
// ... standart call with some query data
success: function(result) {
self.set('somedata', result);
}
});
}
}
});
My route model returns only controller params, so if I get it right as controller properties get updated, there must be a simple step to update the current model and display all changes to the template.
export default Ember.Route.extend({
model: function(params) {
return params;
}
});
Can you guys give me a tip how this process is regularly built in Ember?
You are looking for self.set('model.somedata', results).
Your code as it stands is setting the somedata property on the controller, which doesn't affect anything.
Yeah it's a bit confusing, here's how I think of it...
First Ember sets up the route.
The route has multiple hooks that are used to get and process the model (i.e. beforeModel, model, afterModel). Ember will always look for these as part of it's opinionated nature.
There is also a setupController hook that will fire after all the model hooks. As part of the setupController hook, the controller will be created and the model will be passed to the controller.
Once that happens, I've found it helpful to think of the model as no longer being part of the route, but the controller instead.
Controllers will be deprecated. So IMO do not use controllers.
Handle that action in your route. If you bind value to the object returned by model hook, your data and page will be updated when you update the value.
export default Ember.Route.extend({
somedata: null,
model: function(params) {
return {
params: params,
somedata: this.get('somedata')
};
},
actions: {
getContent: function () {
...
var _this = this;
...
success: function(result) {
_this.set('somedata', result);
}
}
}
});

Ember model with session data

I have a problem with ember when I want to use a session data (companyId that is assigned to a user) in a model. I am using ember-cli and ember 0.13.1 with ember-simple-auth. Lets say I have a route called user/profile when I want to show information about company that the user is assigned to. In order to do that I need to retrieve a companyId that is assigned to a user from session and make a call with a model to my API. Currently the route for user/profile looks like that:
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function() {
return Ember.RSVP.hash({
industries: this.store.findAll('industry'),
industryroles: this.store.findAll('industryrole'),
company: this.store.find('company',this.session.get('user.companyId'))
});
},
setupController: function(controller, model) {
controller.set('industries', model.industries);
controller.set('industryroles', model.industryroles);
controller.set('company', model.company);
}
});
Everything works when you go to that route from different route, but when you are in user/profile route and you press F5 then the this.session.get('user.companyId') is undefined.
Does somebody have an idea how I can solve that problem ?
Best regards
Pawel
It's seems you need to look in session.content setting (if you not overriding beforeModel hook in user/profile route, of course).
If there is two step data fetching in your case, you need to wait for session data in user/profile model hook, and only then you can get user.companyId.
I came up with some solution. I have set an observer for session.user.companyId in route of user/profile. So currently my route looks like that:
//app/routes/user/profile.js
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
onSessionIsAuthenticated: function () {
var isAuthenticated = this.get('session.isAuthenticated');
if (!isAuthenticated) {
return false;
}
var _this = this;
return Ember.RSVP.hash({
company: _this.store.find('company',this.get('session.user.companyId')).then(function (data) {
_this.controllerFor('user.profile').set('company', data);
})
});
}.observes('session.user.companyId'),
model: function() {
return Ember.RSVP.hash({
industries: this.store.findAll('industry'),
industryroles: this.store.findAll('industryrole')
});
},
setupController: function(controller, model) {
controller.set('industries', model.industries);
controller.set('industryroles', model.industryroles);
}
});
The rest industry and industry roles are still left in a controller. Now everything seems correctly but I am not sure if this is the correct solution for this problem. I am not sure if this is what you was in your mind #Microfed?
If someone is still struggling with the problem described above. This is the solution:
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function() {
return Ember.RSVP.hash({
industries: this.store.findAll('industry'),
industryroles: this.store.findAll('industryrole'),
company: this.store.find('company', this.get('session.data.user.companyId'))
});
},
setupController: function(controller, model) {
controller.set('industries', model.industries);
controller.set('industryroles', model.industryroles);
controller.set('company', model.company);
}
});

Ember model hook in Route vs Ember.Object.extend({})

What's the difference between using the model hook in an Ember Route:
App.PhotoRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('/photos/'+params.photo_id);
}
});
Versus creating your own Ember Object?
App.PhotoModel = Ember.Object.extend({});
App.PhotoModel.reopenClass({
find: function(id){
$.ajax({
url: 'https://www.go.get.my.photo',
dataType: 'jsonp',
data: { id: id },
success: function(response){
return response.data;
}
});
}
});
Why would you use one over the other?
One is part of the workflow and the other is a class.
The model hook will provide the model for a route when it's accessed (in that case photo). Additionally it will wait for the async call to complete and use the result of the ajax call.
Example: http://emberjs.jsbin.com/
Extending Ember.Object will define a class for reusability. It's very much a building block for the entire Ember framework.
App.MyModelObject = Ember.Object.extend({});
A more useful example would be
App.MyModel = Ember.Object.extend({
total: function(){
return this.get('val1') + this.get('val2');
}.property('val1', 'val2')
});
var foo = App.MyModel.create({val1:3, val2:5});
console.log(foo.get('total'));
Example: http://emberjs.jsbin.com/xinozi/1/edit
The two are completely different. model on an Ember route is a hook which ember gives you to (fetch data from an api and create an object that holds the data your controller needs and so on) return a promise which gets resolved to the route's controller's model, when the said route is entered and transitioned into. On the other hand, App.MyModel = Em.Object.extend({}) creates a class which is just a template from which objects which are instances of App.MyModel can be instantiated.
If your application wanted to model users, for example, it would have a user "model" like
App.User = Em.Object.extend({username: 'Alice'})
or something similar. However, if you have a user route which looks like /#/user/id, then the model hook on the route would be something like this
model: function(params) {
return new Ember.RSVP.Promise(function(success, failure) {
//make an ajax call and invoke the success and failure handles here appropriately
});

Backbone listener events not working

I have two collections People and Teams.
I would like the Teams collection to listen if there has been a person added to the People collection.
However, I keep getting this error:
Uncaught TypeError: Cannot read property '_listenerId' of undefined
Perhaps I am misunderstanding the concepts of bind and listenTo? Below is the code I'm using for both collections.
var People = Backbone.Collection.extend({
url: '/people',
model: Person,
comparator: 'id',
initialize: function() {
//Why does this return '_listenerID of undefined'
this.bind('add', function() {
var teams = new Teams;
teams.render;
});
},
});
var Teams = Backbone.Collection.extend({
url: '/team',
model: Team,
comparator: 'id',
initialize: function() {
this.listenTo(People.collection, 'add', this.render);
},
render: function() {
console.log("POOP")
}
});
You indeed misunderstand how the listeners work. You're supposed to listen to some object (ie, an instance of a class) in particular.
Here Backbone tries to access the private property of a Backbone object listenerId to do some internal binding stuff. In your case, it's trying to get People.collection._listenerId. People is your class, and it doesn't have any collection property, so People.collection is undefined, thus your error.
Those are the basics of your problem. Having not entirely understood the links between your classes and what you're trying to do, I won't go any further at the moment.

Backbone.js Inherit Views

I have a FormView which handles such events as save and cancel. I also have an AjaxFormView that handles save, cancel and get form by ajax. I have an AssetFormView that handles save, cancel, get by ajax, delete, and print.
So on and so forth. there is considerable repitition.
I found a post this post http://kalimotxocoding.blogspot.com/2011/03/playing-with-backbonejs-views.html
where he shows you can extend views. However, I'm finding that when i have multiple versions of views on the page there are properties cross pollinating. Is there no built in way to inherit views in backbone, that is safe?
Thanks,
Raif
* hmmm well, this stuff is pretty thick and my current cross pollination issue may be ( probably is ) the result of some error on my part, but the question still stands, is there not and would it not be an important feature to have, some way to inherit views?
I'd like to see what you mean when you say that your properties are cross-pollenating.
The View.extend mechanism works quite well. Do be aware, though, that you are extending one prototype with new functions. With prototypical inheritance, the prototype shares it's objects with the new instances.
I am guessing that when you say that your properties are "cross-pollenating", you are actually doing something like this:
var baseModel = Backbone.Model.extend({
defaults: {
foo: { bar: "baz" }
}
});
Since the objects are shared, every instance of baseModel ends up having the same object for foo, giving the feeling of cross-pollination.
If instead, you define your defaults as a function, then each instance will get it's own copy of the foo object and your cross-pollination goes away.
var baseModel = Backbone.Model.extend({
defaults: function() { return {
foo: { bar: "baz" }
} }
});
Of course, without code, we can't be certain to what your problem is. Just know that this mechanism has been well-used among the community without trouble. It is safe. You just need to understand what is going on.
I'm not sure if this is the same problem you're having but I wanted to have some events defined in the parent class and then have the child class extend the property without overriding it.
If I did it like this, I was overriding what the parent defined:
App.parent = Backbone.View.extend({
events: {
'click #button-add': 'onAddButtonClicked'
'click #button-delete': 'onDeleteButtonClicked'
}
onAddButtonClicked: function() {
console.log('onAddButtonClicked');
},
onDeleteButtonClicked: function() {
console.log('onDeleteButtonClicked');
}
});
App.child = App.parent.extend({
initialize: function() {
// This looks redundant but it seems to protect App.parent::events
this.events = _.extend({}, this.additionalEvents, this.events);
// THIS IS WRONG and results in errors when I have multiple childdren:
_.extend(this.events, this.additionalEvents); // <-- this seems to change the parent
}
additionalEvents: {
'click #button-additional': 'onOtherButtonClicked'
},
onOtherButtonClicked: function() {
console.log('child:onOtherButtonClicked');
}
});
When extending the parent's events like this:
_.extend(this.events, this.additionalEvents);
I'd get "Uncaught Error: Method 'onOtherButtonClicked' does not exist" because I was modifying App.parent's events field. App.child2 was blowing up because it couldn't see the events that were put there by App.child.
By changing it to:
this.events = _.extend({}, this.additionalEvents, this.events);
I was able to protect the parent.

Resources