Ember.js accessing and changing variables between controllers - ember.js

I am attempting to write a login/logout widget with ember. I want to toggle the isLoggedIn property such that when a user logs out it is set to False and, True when a user logs in. isLoggedIn is defined in my application controller and called with handlebars in my application template. For now, I need to set the value of of isLoggedIn to true when a user logs in successfully and the Login function is activated inside of LoginController - and logout when the user clicks logout. So my question is: how can I have LoginController & application controller access each other and change variables within them.
Here is some code from the application template:
<section class="top-bar-section">
<!-- Right Nav Section -->
<ul class="right">
...
{{#if isLoggedIn}}
<li><a href="#" {{ action "logout" }}>Logout</a></li>
{{else}}
<li>
{{#linkTo "login"}}Login{{/linkTo}} </li>
{{/if}}
</ul>
</section>
</nav>
{{outlet}}
application controller:
var App;
App = require('app');
module.exports = App.ApplicationController = Ember.ObjectController.extend({
isLoggedIn: false,
logout: function(){
this.set("isLoggedIn", false);
console.log(this.token);
}
});
login template:
...
<form class="form-horizontal" {{action "login" on="submit"}}>
...
<div class="row">
<div class="large-5 columns">
<label>Username</label>
{{input value=username type="text" placeholder="Username"}}
</div>
<div class="large-5 columns">
<label>Password</label>
{{input value=password type="password" placeholder="Password"}}
</div>
<div class="large-2 columns">
</br>
{{input class="small button" type="submit" value="Log In"}}
</div>
</div>
</form>
{{#if errorMessage}}
<div class="large-12 columns alert-box alert">{{errorMessage}}</div>
{{/if}}
{{/if}}
LoginController:
var App;
App = require('app');
module.exports = App.LoginController = Ember.ObjectController.extend({
//some variables...
//other functions...
login: function() {
// set isLoggedIn to true here
...}
});
initially the navbar will see that isLoggedIn is false and therefore show Login. Once you successfully login and click submit, an action will fire off and activate login() inside of LoginController. That is where I want to set isLoggedIn to true such that Logout will appear on the navbar.

Have you tried:
module.exports = App.LoginController = Ember.ObjectController.extend({
needs: ['application']
login: function() {
if (authentification sucess) {
this.set('controllers.application.isLoggedIn', true);
} else {
this.set('controllers.application.isLoggedIn', false);
}
}
});
To access other controllers instances, you use the needs property. Each specified controller will be injected in the controllers property. So needs: ['application'], injects the application controller in controllers.applicaiton.

Related

On submit can't get data from checkbox selection

Hi in my apps I have a form with checkbox group, and I can't retrived selected at submit.
Here is some code
The form content from declare.handlebars:
<form class="declare">
<div class="hidden-fields" style="display:none">
{{view Ember.TextField valueBinding="declaration_type" class="form-control half" type="text"}}
</div>
<fieldset>
...
</fieldset>
<fieldset>
<div class="form-group">
<label>Type de support</label>
<p>
{{render 'publication/declaration_support_types' Sampick.supportTypes}}
</p>
</div>
...
</fieldset>
<div class="actions-bottom">
<button {{action "sendDeclaration" content}} class="button button-select"><i class="icon-download"></i> Confirm</button>
</div>
</form>
The handlebars code for the render of publication/declaration_support_types:
{#each }}
<label class="checkbox-inline">
{{input type="checkbox" name="publication_declaration_support_type" checked=isChecked}} {{ description }}
</label>
{{/each}}
Then I have the following controller for the render 'publication/declaration_support_types':
Sampick.PublicationDeclarationSupportTypesController = Ember.ArrayController.extend({
sortProperties: ['description'],
sortAscending: false,
itemController: 'publicationDeclarationSupportType',
selected: Ember.computed.filterBy('[]', 'isChecked', true),
selectedItems: Ember.computed.mapBy('selected', 'description')
});
Sampick.PublicationDeclarationSupportTypeController = Ember.ObjectController.extend({
isChecked: false,
toggle: function() {
this.toggleProperty('isChecked');
}
});
and finaly the route for the previous html
Sampick.PublicationDeclareRoute = Ember.Route.extend({
actions: {
sendDeclaration: function(content) {
var self = this;
if (content.get("prints") == 1) {
self.validateRecipient(content);
} else {
self.submitDeclaration(content);
}
}
}
});
My issue is that in my sendDeclaration action I can't get the selected checkbox from declarationSupportTypes using the selectedItems propertie define in the controller.
Thanks for your helps
Working fiddle: http://emberjs.jsbin.com/legozucega/1/
There was a typo in IndexRoute, action=>actions.

Ember: How do bindings work in the application template?

I have a boolean variable focusBool that hide the menu of the Web App when True. The Application template with the {{#if}} statement is below:
<script type="text/x-handlebars">
{{#if focusBool}}
<nav class="navbar navbar-default navbar-fixed-bottom" role="navigation">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
{{#link-to 'home' tagName="li"}}
{{#link-to 'home'}}
Home
{{/link-to}}
{{/link-to}}
{{#link-to 'books' tagName="li"}}
{{#link-to 'books'}}
Books
{{/link-to}}
{{/link-to}}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav
{{/if}}
<div class='container-fluid'>
{{outlet}}
</div>
</script>
This variable has been injected to all routes, controllers and components. When the focusBool is changed to true after the Application has loaded, the menu items do not disappear. How can I have the Application template be rerendered when the focusBool is modified? My understanding is that the {{#if focusBool}} would monitor when focusBool value change and show or hide the menu accordingly?
Here more related code:
App.Session = Ember.Object.extend({
user: null,
focusBool: false,
userID: '',
});
Injection:
Ember.Application.initializer({
name: 'login',
initialize: function (container, application) {
App.register('session:main', App.Session.create(), { instantiate: false, singleton: true });
// Add `session` object to route to check user
App.inject('route', 'session', 'session:main');
// Add `session` object to controller to visualize in templates
App.inject('controller', 'session', 'session:main');
// Add `session` object to session to visualize in templates
App.inject('component', 'session', 'session:main');
}
});
The Application Route:
App.ApplicationRoute = Ember.Route.extend({
isAutheticated: null,
actions: {
login: function() {
//var session = this.get('session.');
var isAuth = false;
var store = this.store;
var user = null;
var _this = this;
var firebase = new Firebase("https://dynamicslife.firebaseio.com");
firebase.authWithOAuthPopup('facebook', function(error, authData) {
if (error) {
isAutheticated = false;
console.log('failled login attempt', error);
} else if (authData) {
isAutheticated = true;
console.log('user authenticated with Firebase', authData.uid);
var record = store.find('user', authData.uid).then(function(_user) {
console.log('user exist in Firebase');
_this.session.set('userID', authData.uid);
//_this.rerender();
}, function(error) {
// user does not exist or some other error
store.unloadAll('user');
user = store.createRecord('user', {
id: authData.uid,
uid: authData.uid,
displayName: authData.facebook.displayName,
});
user.save();
_this.session.set('userID', authData.uid)
_this.rerender();
console.log('New user created in Firebase');
});
}
});
},
logout: function() {
var firebase = new Firebase("https://dynamicslife.firebaseio.com");
firebase.unauth();
isAuth = false;
this.session.set('userID', '');
console.log('logged out');
}
}
});
At first glance it seems your referencing focusBool incorrectly in the template. Since you're injecting the session object, you should reference it like session.focusBool in the template.
Here is a working demo.

Ember template does not remove when transition to different route

I feel as if this is a very simple problem to fix I am just not aware of how to fix it. Currently I have an outlet that displays a template like this:
user.hbs:
<div id="user-profile">
<img {{bind-attr src="avatarURL"}} alt="User's Avatar" class="profilePic"/>
<h2>{{name}}</h2>
<span>{{email}}</span>
<p>{{bio}}</p>
<span>Created {{creationDate}}</span>
<button {{action "edit"}}>Edit</button>
{{outlet}}
</div>
The template to be rendered at the outlet is this:
user_edit.hbs:
<div id="user-edit">
<h3>Edit User</h3>
<div class="panel-body">
<div class="row">
<label class="edit-user-label">Choose user avatar</label>
{{input class="form-control" value=avatarUrl}}
</div>
<div class="row">
<label>User name</label>
{{input class="form-control" value=name}}
</div>
<div class="row">
<label class="edit-user-label">User email</label>
{{input class="form-control" value=email}}
</div>
<div class="row">
<label class="edit-user-label">User short bio</label>
{{textarea class="text-control" value=bio}}
<div>
<div class="row">
<button {{action "save"}}>SAVE</button>
<button {{action "cancel"}}>CANCEL</button>
</div>
</div>
</div>
When I first visit the user route, the outlet does not display because the button has not been clicked. The button is hooked to a controller which takes care of the action. The action just transitions to the route where the template is displayed at the outlet. It appears just as expected but when I click on a different user model, the outlet from the previous user is still there without everything in the <div class="panel-body"> </div>. So Ember hides the panel-body div on transition but not the user-edit div. If you need more information I will be happy to provide it.
Here are the controllers:
userController:
App.UserController = Ember.ObjectController.extend({
actions: {
edit: function() {
this.transitionToRoute('user.edit');
}
}
});
Here is the userEditController:
App.UserEditController = Ember.ObjectController.extend({
actions: {
save: function() {
var user = this.get('model');
user.save();
this.transitionToRoute('user', user);
},
cancel: function() {
var user = this.get('model');
this.transitionToRoute('user', user);
}
}
})
Hi why dont you use {{#link-to 'edit' model}} instead of action ???
you can pass model to link-to so you dont have to get model in controller and then transitionToRoute
Look at this

Why would a newly created record not show up in a list?

I have a form that creates a record, then transitions to the list of resources for a particular object. However once the record is created, it is not reflected in the list of resources. If I refresh the page, the record is saved in the correct place. I have the ember chrome extension installed and if I look under Resources, then the resource is there pointing to the correct Badge. But if I go to badge first, and look for resources, it is not listed. Any ideas? I would be happy to provide any more information necessary to clarify. Thank you in advance
Create Resource Form Controller and Route
Controller
App.ResourcesCreateController = Ember.ObjectController.extend({
resourceTypes: ["link","file","video"],
needs: ['badge','resourcesIndex'],
actions: {
save: function() {
//Gather the info from the form
var description = this.get('description');
var url = this.get('url');
var type = this.get('type');
var text = this.get('text');
var badge = this.get('controllers.badge').get('model');
//set the data to the model of the route (ResourceCreateRoute)
var resource = this.get('model');
console.log(resource);
resource.set('description',description);
resource.set('url',url);
resource.set('type',type);
resource.set('text',text);
resource.set('badge',badge);
var self = this;
//save the route
var a = resource.save().then(function() {
//if success
//this.get('store').reload();
console.log('%c that resource saved rather nicely','color:green;');
self.transitionToRoute('resources.index',self.badge);
}, function() {
//if failure
console.log('%c Yea boss...that didnt go so hot', 'color:red;');
self.set('isError',true);
});
},
reset: function() {
this.transitionToRoute('resources.index');
}
}
});
Route
App.ResourcesCreateRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('resource');
}
})
List Resources Route
App.ResourcesRoute = Ember.Route.extend({
model: function(){
return this.modelFor('badge').get('resources');
}
});
Models
Resource Model
App.Resource = DS.Model.extend({
'badge': DS.belongsTo('badge'),
'text': attr('string'),
'url': attr('string'),
'description': attr('string'),
'type': attr('string')
});
Badge Model
App.Badge = DS.Model.extend({
'category': DS.belongsTo('category'),
'title': attr('string'),
'type': attr('string'),
'ord': attr('number'),
'short_description': attr('string'),
'full_description': attr('string'),
'mentor': DS.belongsTo('employee'),
'parent':DS.belongsTo('badge'),
'icon': attr('string'),
'required': attr('boolean'),
'signoff_role': attr('string'),
'created_at': attr('string'),
'updated_at': attr('string'),
'resources': DS.hasMany('resource', { async: true } ),
'quiz': DS.belongsTo('quiz', { async: true } )
});
Templates
List of Resources
{{#link-to "resources.create" class="btn btn-primary btn-xs pull-right"}} Create Resource {{icon "plus"}}{{/link-to}}
<h3>Resources</h3>
<dl>
{{#each resource in controller}}
{{render resources/resource resource}}
{{else}}
<p class="lead text-muted">There are no resources</p>
{{/each}}
</dl>
Resource Item Template
{{#if isEditing}}
<div {{bindAttr class="controller.isError:alert-danger:alert-info :alert"}}>
<div class="row">
<div class="col col-lg-2">
<small>Type</small>
{{view Ember.Select contentBinding="resourceTypes" classNames="form-control" valueBinding="type"}}
</div>
<div class="col col-lg-10">
<small>Resource Name</small>
{{input valueBinding="text" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>Description</small>
{{textarea valueBinding="description" rows="5" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>URL,File Name, or Vimeo ID</small>
{{input valueBinding="url" class="form-control"}}
</div>
</div>
<hr>
<div class="btn-group">
<div {{action "save"}} class="btn btn-primary">{{icon "floppy-save"}} Save</div>
{{#if confirmDelete}}
<div {{action "delete"}} class="btn btn-danger">{{icon "trash"}} Are You sure?</div>
{{else}}
<div {{action "confirm"}} class="btn btn-danger">{{icon "trash"}} Delete</div>
{{/if}}
</div>
<div {{action "reset"}} class="btn btn-default"> {{icon "ban-circle"}} Cancel</div>
</div>
{{else}}
<div class="btn-group pull-right btn-group-xs">
{{#if view.hover }}
<div {{action "edit"}} class="btn btn-default">{{icon "cog"}}</div>
{{/if}}
</div>
<dt>
<span class="text-muted">{{resource_icon type}}</span> {{text}}
</dt>
{{#if description}}
<dd class="text-muted" style="margin-bottom:1em">
{{markdown description}}
</dd>
{{/if}}
<hr>
{{/if}}
Create Resource Template
<h3>Create Resource</h3>
<div class="row">
<div class="col col-lg-2">
<small>Type</small>
{{view Ember.Select contentBinding="resourceTypes" classNames="form-control" valueBinding="type"}}
</div>
<div class="col col-lg-10">
<small>Resource Name</small>
{{input valueBinding="text" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>Description</small>
{{textarea valueBinding="description" rows="5" class="form-control"}}
</div>
</div>
<div class="row">
<div class="col col-lg-12">
<br>
<small>URL,File Name, or Vimeo ID</small>
{{input valueBinding="url" class="form-control"}}
</div>
</div>
<hr>
<div {{action "save"}} class="btn btn-primary">{{icon "floppy-save"}} Save</div>
<div {{action "test"}} class="btn btn">Test</div>
{{#link-to "resources.index" class="btn btn-default" }} {{icon "ban-circle"}} Cancel {{/link-to}}
<br><br>
</div>
Just some general notes first.
With this much code, everyone's going to have a much easier time helping you if you provide a JSBin or something. It's a bit of extra work for you, but you're asking for help, and this is a lot to just mentally parse and run. Personally, it was some extra overhead for me because you didn't include your router, so I had to do a pass just to try to figure out how badge and resource were related.
When you're using an ObjectController with the route model set to a new record, with input helpers, you shouldn't need to do all of that setting. That's why you specified those value bindings on the helpers. But when you do need to set a bunch of properties, you can just do that all at once with something like record.setProperties({prop1: prop1Value, prop2: prop2Value ...}); and save yourself a lot of typing.
I don't understand why you're using resourcesIndex as a ResourcesCreateController need. To actually answer your question, it might work to specify just 'resources' as a need
then use something like
resource.save().then(function(record){
self.get("controllers.resources").pushObject(record);
self.transitionToRoute("resources.index", badge); // I don't know if this makes any sense because you didn't show your router, but at the very least, don't use self.badge, or even self.get("badge"), because badge is already accessible in this scope.
}
It'd be nice to see your Model definitions, and even better if you had a jsbin setup showing the problem.
You could always try hooking it up on createRecord.
App.ResourcesCreateRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('resource', {badge: this.modelFor('badge')});
}
})
It seems like when you create the new resource, you aren't putting it in the store. You should try something like this.store.createRecord(resource) then instead of your resource.save, do a this.store.commit.
I'm not entirely sure I'm right. But it may be a possibility.
Fixed. I am not sure if this is the correct way to handle this but basically the parent model needed to be reloaded once the child model was created and saved. So like this
save: function() {
var self = this;
var resource = this.store.createRecord('resource',{
property: 'property',
relatedProperty: this.get('model'),
//etc
});
resource.save().then(function(){
self.get('model').reload();
},function(){
//do stuff because the resource didnt save
});
}

Change a view property from an unrelated controller

I have the following view:
App.MessageTrayView = Bootstrap.AlertMessage.extend({
message: 'This is a message.',
});
Displayed in this template:
<script type="text/x-handlebars" data-template-name="nodes">
<article>
<form class="form-horizontal">
<fieldset>
{{view App.MessageTrayView id="message-tray-view"}}
<div id="legend" class="">
<legend class="">Nodes <span class="badge">{{controllers.nodesIndex.length}} records</span>
<div class="pull-right">
<a {{action destroyAllRecords}}><i class="icon-remove-circle"></i><a/>
{{#linkTo "nodes.new" class="btn btn-primary"}}Add Node{{/linkTo}}
</div>
</legend>
</div>
{{outlet}}
</fieldset>
</form>
</article>
</script>
And this unrelated controller:
App.NodesIndexController = Ember.ArrayController.extend({
destroyAllRecords: function () {
console.log('destroyAllRecords called');
App.MessageTrayView.set('message', 'All nodes have been deleted');
},
});
I want to change the message displayed as soon as the destroyAllRecords is triggered. This is not working (the error message in the console is telling me that I am doing something * very* wrong). How can I change the message property, so that the changes are directly visible on the page?
You can see the code live here
One quick way of doing this could be to define a property on the App namespace:
App = Ember.Application.create({
messageTrayContent: ''
});
then bind to it in your view using the suffix Binding after your property name:
App.MessageTrayView = Bootstrap.AlertMessage.extend({
messageBinding: 'App.messageTrayContent'
});
Now doing:
App.NodesIndexController = Ember.ArrayController.extend({
destroyAllRecords: function () {
console.log('destroyAllRecords called');
App.set('messageTrayContent', 'All nodes have been deleted');
},
});
should work.
Hope it helps.