Ember Data: How to make AJAX calls in Ember-Objects (has no method 'find' ) - ember.js

I'm trying to make an AJAX call to my API over Ember Data (1.0.0 Beta 4), but I don't know how to access the model outside the router. The documentation provides such examples only:
App.PostRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('post', params.post_id);
}
});
My code:
var AuthManager = Ember.Object.extend({
authenticate: function(accessToken, userId) {
var user = this.store.find('user', userId);
/* ... */
},
/* ... */
});
Now I get has no method 'find':
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = meta(this), proto = m.proto;
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
if (typeof properties !== 'object' && properties !== undefined) {
throw new Ember.Error("Ember.Object.create only accepts objects.");
}
if (!properties) { continue; }
var keyNames = Ember.keys(properties);
for (var j = 0, ll = keyNames.length; j < ll; j++) {
var keyName = keyNames[j];
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
Ember.assert("`actions` must be provided at extend time, not at create time, when Ember.ActionHandler is used (i.e. views, controllers & routes).", !((keyName === 'actions') && Ember.ActionHandler.detect(this)));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
this.init.apply(this, arguments);
m.proto = proto;
finishChains(this);
sendEvent(this, "init");
} has no method 'find'
In Ember Data < 0.14 methods like App.User.find(id) were present but it's deprecated

You can use the dependency injection to inject a store in the AuthManager:
Ember.Application.initializer({
name: "inject store in auth manager",
initialize: function(container, application) {
// register the AuthManager in the container
container.register('authManager:main', App.AuthManager);
// inject the store in the AuthManager
container.injection('authManager', 'store', 'store:main');
// inject the AuthManager in the route
container.injection('route', 'authManager', 'authManager:main');
// inject in the controller
// container.injection('controller', 'authManager', 'authManager:main');
}
});
And in the route you will able to do:
App.IndexRoute = Ember.Route.extend({
model: function() {
this.authManager.authenticate('token', 'userId');
return [];
}
});
See this in action http://jsfiddle.net/marciojunior/3dYnG/

Related

Accessing controller properties within controller function in ember

I'm unable to access the controller property within a controller function:
App.ViewRController = Ember.Controller.extend({
datas:null,
actions:{
viewfile:function() {
let filename = this.get('filename');
let count=0;
let data;
let self = this;
if (filename != null)
{
filename = filename.trim();
if(filename.length > 0)
{
try {
let obj= new XMLHttpRequest();
obj.onreadystatechange = function() {
if(obj.readyState==4 && obj.status ==200)
{
let json = obj.responseText;
console.log(self);
data = JSON.parse(json);
self.set(datas, data);
}
}
let params = "filename=" + filename;
obj.open("POST","view");
obj.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
obj.send(params);
//this.set('datas',data);
}
catch(e)
{
alert(e);
}
}
else
alert("enter filename");
}
else
alert("enter filename");
}
}
The template should iterate over the data object with properties. But I couldn't set the data property within the function in the controller.
You trying to set a property with
self.set(datas,data);
But you need to be setting the "datas" property as a string:
self.set("datas", data);

attributes are not defined with babel 6

I have an Ember app with ember-computed-decorators and I have this kind of model :
import DS from 'ember-data';
import {alias} from 'ember-computed-decorators';
export default DS.Model.extend({
#alias('customData.email') email
});
It worked with ember-cli-babel version 5 but I updated to the version 6 with tranform-decorator-legacy and I have this error :
email is not defined
I reproduced it with a simple js script like this :
function dec(target, name, descriptor) {
const method = descriptor.value;
descriptor.value = function(...args) {
return 'hello';
}
}
const Foo = {
#dec test
}
console.log(Foo.test);
And I have the same error.
This works :
function dec(target, name, descriptor) {
const method = descriptor.value;
descriptor.value = function(...args) {
return 'hello';
}
}
const Foo = {
#dec
test() {
return 'test';
}
}
console.log(Foo.test());
I think #dec test is strange but it worked with babel 5. What's the solution ?
Edit
Here is what's generated by ember :
define('tiny/models/subscription', ['exports', 'ember-data', 'ember-computed-decorators'], function (exports, _emberData, _emberComputedDecorators) {
function _createDecoratedObject(descriptors) { var target = {}; for (var i = 0; i < descriptors.length; i++) { var descriptor = descriptors[i]; var decorators = descriptor.decorators; var key = descriptor.key; delete descriptor.key; delete descriptor.decorators; descriptor.enumerable = true; descriptor.configurable = true; if ('value' in descriptor || descriptor.initializer) descriptor.writable = true; if (decorators) { for (var f = 0; f < decorators.length; f++) { var decorator = decorators[f]; if (typeof decorator === 'function') { descriptor = decorator(target, key, descriptor) || descriptor; } else { throw new TypeError('The decorator for method ' + descriptor.key + ' is of the invalid type ' + typeof decorator); } } } if (descriptor.initializer) { descriptor.value = descriptor.initializer.call(target); } Object.defineProperty(target, key, descriptor); } return target; }
exports['default'] = _emberData['default'].Model.extend(_createDecoratedObject([{
key: 'mail',
initializer: function initializer() {
return _emberData['default'].attr();
}
}, {
key: 'email',
decorators: [(0, _emberComputedDecorators.alias)('mail')],
initializer: function initializer() {
return email;
}
}]));
});
Here is what's generated by ember-cli-babel version 6 :
define('tiny/models/subscription', ['exports', 'ember-data', 'ember-computed-decorators'], function (exports, _emberData, _emberComputedDecorators) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object['ke' + 'ys'](descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc);
desc = null;
}
return desc;
}
var _dec, _desc, _value, _obj, _init;
exports.default = _emberData.default.Model.extend((_dec = (0, _emberComputedDecorators.alias)('mail'), (_obj = { email: email
}, (_applyDecoratedDescriptor(_obj, 'email', [_dec], (_init = Object.getOwnPropertyDescriptor(_obj, 'email'), _init = _init ? _init.value : undefined, {
enumerable: true,
configurable: true,
writable: true,
initializer: function initializer() {
return _init;
}
}), _obj)), _obj)));
});
I have the same result with babel 5.

How to properly setup a store that acts as a single pointer across your web app

I have a home grown store that has a simple identityMap. When I return an array of models from this and bind it to a controllers "model" it reflects what you'd expect
the first time you hit a route it reflects this in the template as
you'd expect
But later if I get this same store instance (it's a singleton) and push an object into the identityMap it doesn't automatically update the previous template
The store itself is super basic (no relationships/ just push objects and get by id)
function buildRecord(type, data, store) {
var containerKey = 'model:' + type;
var factory = store.container.lookupFactory(containerKey);
var record = factory.create(data);
var id = data.id;
identityMapForType(type, store)[id] = record;
return record;
}
function identityMapForType(type, store) {
var typeIdentityMap = store.get('identityMap');
var idIdentityMap = typeIdentityMap[type] || {};
typeIdentityMap[type] = idIdentityMap;
return idIdentityMap;
}
var Store = Ember.Object.extend({
init: function() {
this.set('identityMap', {});
},
push: function(type, data) {
var record = this.getById(type, data.id);
if (record) {
record.setProperties(data);
} else {
record = buildRecord(type, data, this);
}
return record;
},
getById: function(type, id) {
var identityMap = identityMapForType(type, this);
return identityMap[id] || null;
}
getEverything: function(type) {
var identityMap = identityMapForType(type, this);
var keys = Object.keys(identityMap);
var values = [];
for (var i = 0; i < keys.length; i++)
{
var val = identityMap[keys[i]];
values.push(val);
}
return values;
}
});
Ember.onLoad('Ember.Application', function(Application) {
Application.initializer({
name: "store",
initialize: function(container, application) {
application.register('store:main', Store);
application.inject('controller', 'store', 'store:main');
application.inject('route', 'store', 'store:main');
}
});
});
In my model hook (in the find all route lets say) I simply query for each item and push them into the store
//inside my model find method lets say ...
find: function(store) {
var url = "/api/foo";
$.getJSON(url, function(response) {
response.forEach(function(data) {
var model = store.push("foo", data);
}
}
return store.getEverything("foo");
}
So I assumed my controllers' model was this bound array (using a single pointer in memory for this array of models)
Yet when I do this inside a controller submit action it won't re-render that prev view (to show the new item that was added to that store's array)
actions: {
submit: function() {
var foo = {}; // assume this is a real json response or js object
var store = this.get("store");
store.push("foo", foo);
}
}
Because of this today, I'm forced to get the parent controller and "set" / "push" this new object to it's content/model property :(
Anyone know what I'm doing wrong here?
I like homegrown solutions, they generally are easier to work with and meld around what you're working on.
So I'm actually surprised this part is working:
//inside my model find method lets say ...
find: function(store) {
var url = "/api/foo";
$.getJSON(url, function(response) {
response.forEach(function(data) {
var model = store.push("foo", data);
}
}
return store.getEverything("foo");
}
If I read through it I see you make an ajax call, and then return store.getEverything immediately after (without a guarantee that the ajax call has completed). Then inside of getEverything you create a new array called values then iterate the identity map linking up all of the currently available records and return that. At this point your store is unaware of this array going forward. So any changes to your store wouldn't get pushed out to the array, they might make it into the identity map, but it isn't feeding the getEverything array.
There are a couple of solutions, one would be to keep track of your everything array. That collection would be super cheap to build, more expensive to search, so keeping the identity map as well would be super beneficial. You could follow your same pattern, but one collection would be the map, whereas the other would be an array of everything.
Modified Build Record
function buildRecord(type, data, store) {
var containerKey = 'model:' + type;
var factory = store.container.lookupFactory(containerKey);
var record = factory.create(data);
var id = data.id;
identityMapForType(type, store)[id] = record;
everythingArrayForType(type, this).pushObject(record);
return record;
}
Copy paste, possibly could be refactored
function everythingArrayForType(type, store) {
var everythingArrays = store.get('everythingArrays');
var arr = everythingArrays[type] || [];
everythingArrays[type] = arr;
return arr;
}
Slightly modified Store
var Store = Ember.Object.extend({
init: function() {
this.set('identityMap', {});
this.set('everythingArrays', {});
},
push: function(type, data) {
var record = this.getById(type, data.id);
if (record) {
record.setProperties(data);
} else {
record = buildRecord(type, data, this);
}
return record;
},
getById: function(type, id) {
var identityMap = identityMapForType(type, this);
return identityMap[id] || null;
}
getEverything: function(type) {
return everythingArrayForType(type, this);
}
});

How do I test an event has been broadcast in AngularJS?

I am just wondering how can I test the handleAddClientBroadcast event?
I have a navigation service like so:
angular.module("ruleManagement.services")
.factory('navigationService', function ($rootScope) {
var navigationService = {};
navigationService.prepForBroadcast = function() {
this.broadCastIsAddClientItem();
};
navigationService.broadCastIsAddClientItem = function() {
$rootScope.$broadcast('handleAddClientBroadcast');
};
return navigationService;
});
I inject this navigation service into my clientsCtrl and catch the handleAddClientBroadcast like so:
$scope.$on('handleAddClientBroadcast', function () {
$scope.clientModel = {
id: 0,
name: "",
description: "",
rules: []
};
var lastClient = _.findLast($scope.clients);
if (typeof lastClient == 'undefined' || lastClient == null) {
lastClient = $scope.clientModel;
}
$scope.clientModel.id = lastClient.id + 1;
$scope.clients.push($scope.clientModel);
});
Thanks.
Assuming you're using Jasmine
spyOn($rootScope, '$broadcast').andCallThrough();
...
expect($rootScope.$broadcast).toHaveBeenCalledWith('eventName');

How to update record in local storage using ember data and localstorage adapter?

I am new to emberjs and making one simple CRUD application. I am using ember data and localstorage-adapter to save record in local storage of browser.
I am trying to update record using localstorage-adapter but it is throwing error.
I have listed my code here :
updatecontact: function(){//save data in local storage
var fname = this.obj_form_edit_data.get('cont_data.fname');
var lname = this.get('cont_data.lname');
var email = this.get('cont_data.email');
var contactno = this.get('cont_data.contactno');
var gendertype = ((this.get('isMale') == true) ? true : false);
var contactype = $(".selectpicker").val();
Grid.ModalModel.updateRecords({
fname: fname,
lname: lname,
email: email,
contactno: contactno,
gendertype: gendertype,
contactype: contactype
});
this.get('store').commit();
}
I am getting following error using above code :
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = meta(this);
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
for (var keyName in properties) {
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
delete m.proto;
finishChains(this);
this.init.apply(this, arguments);
} has no method 'updateRecords'
I am using following code to create new record which working fine :
savecontact: function(){//save data in local storage
var fname = this.obj_form_edit_data.get('cont_data.fname');
var lname = this.obj_form_edit_data.get('cont_data.lname');
var email = this.obj_form_edit_data.get('cont_data.email');
var contactno = this.obj_form_edit_data.get('cont_data.contactno');
var gendertype = ((this.get('isMale') == true) ? true : false);
var contactype = $(".selectpicker").text();
Grid.ModalModel.createRecord({
fname: fname,
lname: lname,
email: email,
contactno: contactno,
gendertype: gendertype,
contactype: contactype
});
this.get('store').commit();
}
You're using updateRecords as a plural, it should be updateRecord