I'm getting different behaviours from session.isAuthenticated depending on how I access it. This is probably only a symptom of my real problem.
What happens:
click sign in button
window pops up, prompts for login
login, popup goes away
templates that rely on
session.isAuthenticated do not change display state (ie, continue
acting as if it is false or undefined)
if I console.log
this.get('session.isAuthenticated') I get true
When this was just torii everything was working, so I've messed up when adding ember-simple-auth somehow.
I get several "DEPRECATION: Using the injected 'container' is deprecated. Please use the 'getOwner' helper instead to access the owner of this object warnings but when it was just torii it worked fine with these warning so I assume they still aren't a problem.
templates/application.hbs
<header>
<nav class="nav-main">
{{#if session.isAuthenticated}}
<button {{action 'invalidateSession'}}>Sign out</button>
{{else}}
<button {{action 'authenticate'}}>Sign in</button>
{{/if}}
Auth: {{session.isAuthenticated}}<br>
<button {{action 'icanhaz'}}>test?</button>
</nav>
</header>
routes/application.js
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
session: Ember.inject.service('session'),
actions: {
authenticate() {
this.get('session').authenticate('authenticator:torii', 'myprovider');
},
invalidateSession() {
this.get('session').invalidate();
},
icanhaz() {
console.log(this.get('session.isAuthenticated'));
}
}
});
adapters/application.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
host: 'https://myprovider.herokuapp.com',
namespace: 'api/v2'
});
authenticators/torii.js
import Ember from 'ember';
import ToriiAuthenticator from 'ember-simple-auth/authenticators/torii';
export default ToriiAuthenticator.extend({
torii: Ember.inject.service(),
})
environment.js
module.exports = function(environment) {
var ENV = {
modulePrefix: 'my-app-prefix',
environment: environment,
baseURL: '/',
locationType: 'auto',
},
contentSecurityPolicy: {
'connect-src': "'self' myprovider.herokuapp.com",
},
torii: {
providers: {
'myprovider': {
apiKey: '6T7hlTUqfYMgcxkXPAOeNzVmC5L26bTYe9A8D5fc',
scope: 'read write',
redirectUri: 'http://localhost:4200'
}
}
}
};
};
You have to inject the session service's into all controllers/components that back templates you want to use the session in. As you're using the session in the application template you'd have to inject the session service in the application controller. Injecting it in the application route does not make it available in the template.
Related
I'm having an issue with Vue. At this point I've read things like this detailing this error occurring when you try to define a method on the root instance, then reference it in local scope.
My issue is slightly different because it is defined in local scope, so I'm not sure what to make of this error. I've also looked at this, and this.
Here is my App.vue:
<template>
<div id="app">
<router-link to="/Home">home</router-link>
<router-link to="/Login">login</router-link>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
console.log('app.vue loading')
</script>
My main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import store from './store'
import router from './router'
import Home from './components/Home'
import Login from './components/Login'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App, Home, Login },
template: '<App/>'
})
console.log('main js loading');
The component the issue is coming from:
<template>
<div class="login">
<head>
<title>{{title}}</title>
</head>
<form for="login" id="loginMain">
<label for="username">Username:</label>
<input type="text" id="username"></input>
<label for="password">Password:</label>
<input type="password"></input><br/><br/>
<button for="login" #click="processLogin">LOGIN</button>
<button for="logout" #click="processLogout">LOGOUT</button>
</form>
<p> Your login status: {{ loginStatus }} </login></p>
</div>
</template>
<script>
import Vue from 'vue'
import { mapGetters, mapActions, Vuex } from 'vuex'
import store from '#/store'
const Login = {
delimiters: [ '[{','}]'],
data () {
title: 'Foo',
msg: 'Bar'
}
name: 'Login',
props: {
// worry about this later
},
methods: {
...mapActions({
processLogin : 'LOGIN_ACTION',
processLogout : 'LOGOUT_ACTION'
})
},
computed: {
...mapGetters({
title: 'GET_TITLE',
loginStatus: 'GET_LOGIN_STATUS'
}),
}
}
console.log('Login loading');
export default Login
And although I'm unsure if it is related, but my store:
import Vue from 'vue'
import Vuex from 'vuex'
import Home from './components/Home'
import Login from './components/Login'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
title: 'My Custom Title',
loggedIn: false
},
mutations: {
MUT_LOGIN: (state) => {
state.loggedIn = true
},
MUT_LOGOUT: (state) => {
state.loggedIn = false
}
},
actions: {
LOGIN_ACTION ({ commit }){
store.commit('MUT_LOGIN')
},
LOGOUT_ACTION ({ commit, state }) {
store.commit('MUT_LOGOUT')
}
},
getters: {
GET_LOGIN_STATUS: state => {
return state.loggedIn
},
GET_TITLE: state => {
return state.title
}
}
})
console.log('store loading');
export default store
I know that I had some of these errors at one point and got rid of them by revisiting each import statement and making some corrections to how I had things hooked up. The only other difference between now and then is that I am running all of these files through Webpack and serving them with Django in a template. (The reason for all the consoles, making sure all the files got in there via Webpack.)
The image below is a couple of the specific things it is barking about.
Because of the link from the devtools error, I have looked at this and also experimented with adding local data () properties to the component itself.
Changing
data () {
title: 'Foo',
msg: 'Bar',
},
to
data () {
return {
title: 'Foo',
msg: 'Bar',
}
},
will fix the "title" error.
As for the issue with the actions nothing is jumping out at me. Can you try mapGetters and see if you have access to the getters?
I'm trying to create a modal for my users to signin, so I have this link:
<li><a {{action "signin"}}>Sign In</a></li>
in a {{planhw-navbar}} component.
{{planhw-navbar signin=(action "showModal" name="signin-modal")}}
But when I open my browser, I get the error:
An action named 'showModal' was not found in (generated application controller)
I've tried putting an action in a controller, a route, and a component:
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
showModal: function(name) {
this.render(name, {
into: 'application',
outlet: 'modal'
});
}, //...
}
})
My component, {{signin-modal}}, works correctly.
My entire application.hbs:
{{planhw-navbar signin=(action "showModal" name="signin-modal")}}
{{outlet}}
{{outlet 'modal'}}
You need to add an action called showModal in your application.js controller.
I'm getting familiar with ember and can't seem to debug an issue I'm having with a generated resource. After generating a new app with ember-cli, I generated a resource:
ember generate resource contacts
This generated a few files, all seemed well. The problems began when attempting to list the contacts from a http-mock. I can see from the network tab in dev tools that the records are retrieved, but the view won't display them:
Model:
#app/models/contact.js
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string')
});
Route:
#app/routes/contacts.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.get('store').find('contact');
}
});
The view
#app/templates/contacts.hbs
<h2 id="title">Contacts</h2>
{{#each contact as |c|}}
{{c.title}}
{{/each}}
{{outlet}}
JSON from http mock:
{
"contacts": [
{
"id": 1,
"title": "Test"
}
]
}
I'm not sure what I'm doing wrong, any help is appreciated.
Found the answer, the problem was in my view contacts.hbs. Changed to:
<h2 id="title">Contacts</h2>
{{#each model as |c|}}
{{c.title}}
{{/each}}
{{outlet}}
Thanks to #jacefarm on this question for putting me on the right track
I'm building a simple application using Ember v1.8.1, Ember CLI 0.1.15 and Ember Data 1.0.0-beta.15 in an attempt to learn the basics of Ember.js. Data is coming from a simple REST API, so I'm using the DS.RESTAdapter and DS.RESTSerializer. The payload is normalized in the serializer, because the API doesn't properly wrap its data in the required hashes.
I'm trying to implement an edit route, but am unable to populate the form fields of that template or even display a simple value. My index route, displaying a list of posts, works as expected, but edit doesn't for some reason.
Using {{title}} in my template doesn't render the value, but outputs a string of unexpected data, something along the lines of: <my-app#model:post::ember413:21>. I'm probably passing something improperly from the route or elsewhere, but I don't know what and where exactly.
Below is some of my relevant code:
app/router.js
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('login');
this.resource('posts', function() {
this.route('add');
this.route('view', { path: ':post_id'});
this.route('edit', { path: ':post_id/edit'});
});
});
export default Router;
app/routes/posts/edit.js
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function(params){
return this.store.find('post', params.post_id);
}
});
app/routes/posts/index.js
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function(){
return this.store.find('post');
}
});
app/controllers/posts/edit.js
import Ember from 'ember';
export default Ember.ObjectController.extend();
app/serializers/application.js
import Ember from "ember";
import DS from "ember-data";
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
primaryKey: 'ID',
attrs: {
author: { embedded: 'always' }
},
extractSingle: function(store, type, payload, id) {
payload = { post : payload };
return this._super(store, type, payload, id);
},
extractArray: function(store, type, payload) {
payload = { posts : payload };
return this._super(store, type, payload);
},
serializeIntoHash: function(hash, type, record, options) {
Ember.merge(hash, this.serialize(record, options));
}
});
app/templates/edit.hbs
<div class="page">
<div class="page-content">
<h2>{{title}}</h2>
{{{content}}}
</div>
</div>
app/templates/index.hbs
<div class="page">
<div class="page-content">
<ul>
{{#each}}
<li>
<h2>{{title}}</h2>
{{{content}}}
</li>
{{/each}}
</ul>
</div>
</div>
Having looked at some examples the above should work, so probably there's an error in the serializer. In the route I can access values from the store however. The following logs the expected data when used in the model method of my edit.js route:
this.store.find('post', params.post_id).then(function(post) {
console.log(post.get('title'));
});
UPDATE: Here is a JS Bin recreating most of the application without Ember CLI. The behaviour is exactly the same as on my local machine; the edit route doesn't properly show the content value (title works, for some reason). When I swap to fixtures instead of the API output the problem remains the same. Also the model in the bin doesn't map to all the values returned by the API, but that doesn't seem to make a difference.
UPDATE 2: Seems the content entry of the returned payload is causing the problems, when I rename the key it's displayed correctly in the edit template. Didn't realise this before, because I was so focussed on getting the content value to display and forgot to test other values. Not sure why it happens only happens on a nested route though and with that particular keyword.
UPDATE 3: If I understand correctly, this might be related to an outstanding issue for Ember Data, there's a proposal for reporting such collisions as well.
I'm creating a router-based EmberJS app (strongly modelled on the excellent router guide). However, I'm quite muddled over what belongs in a view vs in a controller.
I totally get that {{action showFoo}} often indicates a state change and that the Router is the state machine for my app. But some of my actions don't fall into that category.
Here's an example from my actual code (html simplified but mustaches intact). I want to have a login form that works via ajax (i.e. the html form doesn't post directly to the server, it tells my ember app to attempt a login via json).
<form>
Email Name: {{view Ember.TextField valueBinding="email"}}
Password: {{view Ember.TextField valueBinding="password"}}
<button type="submit" {{ action logIn target="this" }}>Sign in</button>
</form>
The valueBindings are fields in my loginController but the logIn handler is in my view (because I couldn't figure out how to tell the template to call the controller). I feel like this is a weird distribution & I'm not sure what the right Ember approach is to this.
I don't think the router should be handling the action because requesting a login attempt isn't really a state change. The loginController feels like the right place to try the login. After a login response is received then that controller could trigger the state change.
I don't think the router should be handling the action because requesting a login attempt isn't really a state change.
I think that's exactly the case: attempting a login should transition to an authenticating state, where for example another click to "login" is ignored.
So IMHO this should be handled by the router. I'm thinking about something like this, see http://jsfiddle.net/pangratz666/97Uyh/:
Handlebars:
<script type="text/x-handlebars" >
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="login" >
<p class="info">{{message}}</p>
Login to view the admin area <br/>
Email: {{view Ember.TextField valueBinding="email" }} <br/>
Password: {{view Ember.TextField valueBinding="password" }} <br/>
<button {{action login}} >Login</button>
</script>
<script type="text/x-handlebars" data-template-name="authenticating" >
Communicating with server ...
</script>
<script type="text/x-handlebars" data-template-name="admin" >
Hello admin!
</script>
JavaScript:
App = Ember.Application.create();
App.ApplicationController = Ember.Controller.extend({
login: function() {
// reset message
this.set('message', null);
// get data from login form
var loginProps = this.getProperties('email', 'password');
// simulate communication with server
Ember.run.later(this, function() {
if (loginProps.password === 'admin') {
this.set('isAuthenticated', true);
this.get('target').send('isAuthenticated');
} else {
this.set('message', 'Invalid username or password');
this.set('isAuthenticated', false);
this.get('target').send('isNotAuthenticated');
}
}, 1000);
// inform target that authentication is in progress
this.get('target').send('authenticationInProgress');
},
logout: function() {
this.set('isAuthenticated', false);
}
});
App.ApplicationView = Ember.View.extend({
templateName: 'application'
});
App.LoginView = Ember.View.extend({
templateName: 'login'
});
App.AdminView = Ember.View.extend({
templateName: 'admin'
});
App.AuthenticatingView = Ember.View.extend({
templateName: 'authenticating'
});
App.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
loggedOut: Ember.Route.extend({
route: '/',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('login');
},
login: function(router) {
router.get('applicationController').login();
},
authenticationInProgress: function(router) {
router.transitionTo('authenticating');
}
}),
authenticating: Ember.State.extend({
enter: function(router) {
router.get('applicationController').connectOutlet('authenticating');
},
isAuthenticated: function(router) {
router.transitionTo('loggedIn');
},
isNotAuthenticated: function(router) {
router.transitionTo('loggedOut');
}
}),
loggedIn: Ember.Route.extend({
route: '/admin',
connectOutlets: function(router) {
if (!router.get('applicationController.isAuthenticated')) {
router.transitionTo('loggedOut');
}
router.get('applicationController').connectOutlet('admin');
},
logout: function(router) {
router.get('applicationController').logout();
}
})
})
})
});
You can use the controller for this, the template your using have access to the controller.
<script type="text/x-handlebars" data-template-name="loginTemplate">
{{#if controller.login}}
Logged in!
{{else}}
Login failed
{{/if}}
</script>
This fiddle shows a small app, which does that: fiddle
Then after login has occured you can make an actioncall to your router, or show the user that login failed.
I have just made it done by change the codes as:
{{ action logIn target="controller" }}