can-model cannot getting data from .json file - javascript

I am trying to implement MVC using AMD in canjs. For that I am using requirejs.
This is my domains.json file:
[
"1":{"uid": "1","urls": "domain1.abc.com"},
"2":{"uid": "2","urls": "domain2.abc.com"},
"3":{"uid": "3","urls": "domain3.abc.com"}
]
This is my domainModel:
define(['can'], function(can){
SearchModel= can.Model({
id: 'uid',
findAll: 'GET /domains.json'
},{})
return SearchModel;
})
This is my controller:
define(['can','jquery'],function(can,$){
domainController=can.Control({defaults:{view:"../view/search.hbs" }},
{
init : function(element,options){
this.element.html(can.view(this.options.view,{
searchlist : this.options.search
}))
}
});
return domainController;
}
This is my main js:
equirejs(['can','controller/domainController','model/domainModel'],
function(can, domainController,domainModel) {
var Application = can.Control.extend({
defaults :{ }
},{
init: function(element,options){
console.log('loaded');
domainModel.findAll({}, function(domains){
domainObject:{searchdomains : domains}
new domainController('#search',domainObject)
});
}
})
return Application;
});
I am tracing out my code.I put breakpoints.On model breakpoints I am not getting values in local variables in chrome devtools.
The url property has 'undefined/{id}' value and findAll method having four properties i.e. arguments,caller,length and name having a value null, null, 0 and "" respectively
I have checked my url of model by navigating through localhost on browser and it is correct.
Then why model cannot getting the values of json file?

You should get an error message since your data is not what Model expects for findAll. Your JSON should be an array (or at least have a length property):
[
{"uid": "1","urls": "domain1.abc.com"},
{"uid": "2","urls": "domain2.abc.com"},
{"uid": "3","urls": "domain3.abc.com"}
]
You also probably want to set the id property in you SearchModel to uid:
define(['can'], function(can){
SearchModel= can.Model({
id: 'uid',
findAll: 'GET /domains.json'
},{})
return SearchModel;
})

Related

Uncaught TypeError: b.toLowerCase is not a function

I am fetching some records :
$companies = \App\User::where('type', 'pet-salon')->orWhere('type', 'veterinarian')->get();
return response()->json($companies);
The data coming back is an array of objects:
[{
id: 2,
type: "xxx",
deactivate: 0,
xxx: "Hello",
middle_name: "Mid",
lastname: "xxx",
//...
}]
This is the jQuery typeahead code:
$('#getCompaniesForConnection').typeahead({
source: function (query, process) {
return $.get('/all/companies', { query: query }, function (data) {
return process(data);
});
}
});
The exception its giving me :
Uncaught TypeError: b.toLowerCase is not a function
And the results drop-down is not showing too, What am i missing here ?
Yes, you need to json_encode your companies.
$companies = \App\User::where('type', 'pet-salon')->orWhere('type',
'veterinarian')->get();
$result = json_encode($companies ); // return this or echo
First, the PHP code looks like it's a laravel code, so you can just return the $companies variable like so:
$companies = \App\User::where('type', 'pet-salon')->orWhere('type', 'veterinarian')->get();
return $companies;
Since models and collections are converted to JSON when cast to a string, you can return Eloquent objects directly from your application's routes or controllers.
And also, let's see the definition of the process function to be sure that's not where the error is coming from.

Populate Backbone.js JSON response into nested collections inside nested collections/models

My problem is that I am just starting out with Backbone.js and are having trouble wrapping my head around a complex problem. I want to save a form that have infinite fields, and some of the fields also needs to have infinite options. I'm just worried I might have started at the wrong end with a JSON response, instead of building the models/collections first. Here is a short pseudocode of what I try to achieve.
id:
parent: <blockid>
fields: array(
id:
title:
helpertext
options: array(
id:
type:
value:
)
)
Currently I am working with a faked JSON response from the server, which I built from scratch, and now I want to divide it into models and collections on the client side.
//Fake a server response
var JSONresponse = {
"formid":"1",
"fields":[
{
"fieldid":"1",
"title":"Empty title",
"helper":"Helper text",
"type":"radio",
"options":[
{
"optionid":"1",
"value":"Empty option.."
},
{
"optionid":"2",
"value":"Empty option.."
}
]
},
{
// fieldid2
}
]
};
The idea is to add fields as I see fit, and then if the field type is radio/checkbox/ul/ol there must also be an "options" array within the field.
My work so far:
var app = {};
app.Models = {};
app.Collections = {};
app.View = {};
app.Models.Option = Backbone.Model.extend({
});
app.Collections.Options = Backbone.Collection.extend({
model: app.Models.Option
});
app.Models.Field = Backbone.Model.extend({
options: new app.Collections.Options()
});
app.Collections.Fields = Backbone.Collection.extend({
model: app.Models.Field
});
app.Models.Form = Backbone.Model.extend({
formid : "1",
fields: new app.Collections.Fields(),
initialize: function() {
}
});
How do I split up my JSON response into all these models and collections?
(Perhaps I should re-evaluate my approach, and go for something like form.fieldList and form.optionList[fieldListId] instead. If so, how would that look like?)
Edit: Here is a little jsfiddle after many fixes, but I still don't really know how to make the inner options list work.
The easiest solution would be using Backbone Relational or Backbone Associations.
The documentation should be enough to help you get started.
If you don't want to use a library you could override the parse function on the Form model.
app.Models.Form = Backbone.Model.extend({
defaults: {
fields: new app.Collections.Fields()
},
parse: function(response, options) {
return {
formid: response.formid,
fields: new app.Collections.Fields(_.map(response.fields, function(field) {
if (field.options) {
field.options = new app.Collections.Options(field.options);
}
return field;
}))
};
}
});
Now if you fetch a form from the server, the response will be parsed into an object graph of models and collections.
form.get('fields') will return an app.Collections.Fields collection. form.get('fields').first().get('options') will return an app.Collections.Options collection, if any options exist.
Also, you could create the form model like this:
var form = new app.Models.Form(JSONresponse, {
parse: true
});
This would result in the same object structure.
It's quite hard to handle the case of nested models and collections right in plain Backbone.
Easiest way of handling this will be something like this:
var Option = Nested.Model.extend({
idAttribute : 'optionid',
defaults : {
optionid : Integer
value : ""
}
});
var Field = Nested.Model.extend({
idAttribute : 'fieldid',
defaults : {
fieldid : Integer,
title : "",
helper : "",
type : "radio",
options : Option.Collection
}
});
var Form = Nested.Model.extend({
idAttribute : 'formid',
defaults : {
formid: Integer,
fields: Field.Collection
});
https://github.com/Volicon/backbone.nestedTypes
And that's it. Yep, you'll get direct access to the attributes as free bonus, just form.fields.first().options.first().value, without that get and set garbage.

MongoDB _id as a String (MEAN Stack)

TL;DR - I am trying to use a collected value from a form input as a document _id but am getting a 404.
I've got a modal that opens and collects form data. My first input in the form is:
<input type="text" id="name" name="name" data-ng-model="name" />
When I try to modify the Mongo (Mongoose) model, to use name as the _id, the form wont post. I get a 404 from http://sitegoeshere/#!/somethings/whatever_i_type_in_for_name
Example model:
var SomethingSchema = new Schema({
_id: {
type: String,
default: 'default',
trim: true
}
}
mongoose.model('Something', SomethingSchema);
And in my Angular controller:
$scope.create = function() {
// Create new Something object
var something = new Somethings ({
_id: this.name
});
// Redirect after save
something.$save(function(response) {
$location.path('somethings/' + response._id);
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
I've been told that MongoDB allows Strings as the _id type so what gives? Any ideas?
UPDATE: Here's something strange, too. I wanted to see if maybe this was a limitation or bug of Mongoose so I got into the database and created two documents:
> db.subnets.find().pretty()
{ "_id" : ObjectId("546bef63395b0694d51b5cbe"), "description" : "description!" }
{ "_id" : "mystring", "description" : "more description!" }
When I go to my app and try to pull their individual views up, I can see the data for my custom _id document but get a 500 Internal Server Error when I try to access the other.
GET http://localhost:3000/somethings/546bef63395b0694d51b5cbe 500 (Internal Server Error)
GET http://localhost:3000/somethings/mystring 200 OK
The problem is most likely with this.name - looks like it's undefined.

Emberjs - Calling directly nested resource's URL

I have merged together from this two problem (How to pass model in Nested routes - emberjs and Embedded data from RestApi) a JsBin example: http://jsbin.com/OxIDiVU/544
It works fine if you navigate customers-> info -> contact, but it will break if one calls directly a customer's contact eg.:http://jsbin.com/OxIDiVU/544#/customers/3/contact
Error while loading route: customer.contact Cannot set property 'store' of undefined TypeError: Cannot set property 'store' of undefined
When you do a request for a single record, it uses a different serializer endpoint and expects the data in a different format. The format it expects is:
{
customer: {
id: 1,
currency:1
},
currencies: [
{
id:1,
prop: 'foo'
}
]
}
And the endpoint in the serializer is extractSingle. Feel free to extract out the portions of extractArray that are similar and share those.
Pretending your payload is:
{
customer:{
id:3,
name:"Joue",
currency:{
id:5,
iso_code:"BDT"
}
}
}
Your extractSingle would be
extractSingle: function(store, type, payload, id) {
var customer = payload.customer,
currencies = [];
var currency = customer.currency;
delete customer.currency;
if(currency){
currencies.push(currency);
customer.currency = currency.id;
}
payload = { customer:customer, currencies: currencies };
return this._super(store, type, payload, id);
}
Here's the example, with a response for customer 3
http://jsbin.com/OxIDiVU/545#/customers/3/contact
your property name should match inside the model, and the root name (currencies here) should be the plural version of the type of record it is.
{
customer: {
id: 1,
default_currency:1
},
currencies: [
{
id:1,
prop: 'foo'
}
]
}

Ember-Data: How do "mappings" work

I'm currently trying to put something together with ember + emberdata + router + asp.net web api. Most of it seem to work, however I stuck in an error message I get when ember-data tries to findAll through the adapter for my models.
In my backend I have a model like this (C#):
public class Genre {
[Key]
public int Id { get; set; }
[Required]
[StringLength(50, MinimumLength=3)]
public string Name { get; set; }
}
Which in my app I represent it like this using ember-data:
App.Genre = DS.Model.extend({
id: DS.attr("number"),
name: DS.attr("string")
}).reopenClass({
url: 'api/genre'
});
I have also a Store defined in my App using the RESTAdapter like so:
App.store = DS.Store.create({
revision: 4,
adapter: DS.RESTAdapter.create({
bulkCommit: false
})
});
And the store is used in my controller as below:
App.GenreController = Ember.ArrayController.extend({
content: App.store.findAll(App.Genre),
selectedGenre: null
});
The router is defined as
App.router = Em.Router.create({
enableLogging: true,
location: 'hash',
root: Ember.Route.extend({
//...
genre: Em.Route.extend({
route: '/genre',
index: Ember.Route.extend({
connectOutlets: function (router, context) {
router.get('applicationController').connectOutlet('genre');
}
})
}),
//...
})
})
When I run my application, I get the following message for every object that has this same structure:
Uncaught Error: assertion failed: Your server returned a hash with the
key 0 but you have no mappings
For reference, here's the json the service is returning:
[
{
"id": 1,
"name": "Action"
},
{
"id": 2,
"name": "Drama"
},
{
"id": 3,
"name": "Comedy"
},
{
"id": 4,
"name": "Romance"
}
]
I cannot tell exactly what the problem is and since the assertion is mentioning that I need mapping, I'd like to know:
What this mapping is and how to use it.
Since the returned json is an array, should I be using a different type of controller in my app ,or is there anything I should know about when working with this type of json in ember-data? or should I change the JsonFormatter options in the server?
Any help is welcome.
I can definitely add more information if you feel this isn't enough to understand the problem.
EDIT: I've changed a few things in my backend and now my findAll() equivalent action in the server serializes the the output as the following json:
{
"genres": [
{ "id": 1, "name": "Action" },
{ "id": 2, "name": "Drama" },
{ "id": 3, "name": "Comedy" },
{ "id": 4, "name": "Romance" }
]
}
But I still can't get it to populate my models in the client and my error message has changed to this:
Uncaught Error: assertion failed: Your server returned a hash with the
key genres but you have no mappings
Not sure what else I might be doing wrong.
The method that throws this exception is sideload and checks for the mappings like this:
sideload: function (store, type, json, root) {
var sideloadedType, mappings, loaded = {};
loaded[root] = true;
for (var prop in json) {
if (!json.hasOwnProperty(prop)) { continue; }
if (prop === root) { continue; }
sideloadedType = type.typeForAssociation(prop);
if (!sideloadedType) {
mappings = get(this, 'mappings');
Ember.assert("Your server returned a hash with the key " + prop + " but you have no mappings", !!mappings);
//...
This call sideloadedType = type.typeForAssociation(prop); returns undefined and then I get the error message. The method typeForAssociation() checks for the for 'associationsByName' key which returns an empty Ember.Map.
Still no solution for this at the moment.
By the way...
My action is now like this:
// GET api/genres
public object GetGenres() {
return new { genres = context.Genres.AsQueryable() };
}
// GET api/genres
//[Queryable]
//public IQueryable<Genre> GetGenres()
//{
// return context.Genres.AsQueryable();
//}
I had to remove the original implementation which gets serialized by json.NET as I could not find config options to produce a json output as Ember-Data expects ( as in {resource_name : [json, json,...]}). Side effect of this is that I've lost built-in OData support, but I'd like to keep it. Does anyone know how could I configure it to produce different json for a collection?
The mapping can be defined in the DS.RESTAdapter. I think you could try to define something like this:
App.Store = DS.Store.extend({
adapter: DS.RESTAdapter.create({
bulkCommit: true,
mappings: {
genres: App.Genre
},
// you can also define plurals, if there is a unregular plural
// usually, RESTAdapter simply add a 's' for plurals.
// for example at work we have to define something like this
plurals: {
business_process: 'business_processes'
//else it tries to fetch business_processs
}
}),
revision: 4
});
Hope this resolves your problem.
Update:
At this time, this is not well documented, I don't remember if we found it by ourself reading the code, or perhaps Tom Dale pointed on it.
Anyway, here is the point for plurals
For the mappings, I think we were driven by the same error as you, and either we tried, either Tom teached us about this.
The RESTAdapter expects the returned JSON to be of the form:
{
"genres": [{
"id": 1,
"name": "action"
},{
"id": 2,
"name": "Drama"
}]
}
The tests are a good source of documentation, see https://github.com/emberjs/data/blob/master/packages/ember-data/tests/unit/rest_adapter_test.js#L315-329
I'm using Ember Data rev. 11 and it seems that the plurals config in DS.RESTAdapter.create never works. I looked into the codes and found a solution as following:
App.Adapter = DS.RESTAdapter.extend({
bulkCommit: false
})
App.Adapter.configure('plurals', {
series: 'series'
})

Categories