I apologize for being new with backbone but I think I get the concepts.
For example: you have models for a Bookstore > Bookshelf > Book > Page
How would you organize this so the views can be controlled like this:
Bookstore.render() //to view the bookstore
Bookstore.bookshelf.get(shelfId).render() //to view shelf
Bookstore.bookshelf.get(shelfId).book.get(bookId).render() //to view book
Bookstore.bookshelf.get(shelfId).books.get(bookId).pages.at(0).render() //to view page
Is this the correct way to do it?
Yes It's possible and I created for you a working example, I think It's just an other method to organize your Backbone JS architecture and I think It's a pretty good idea.
Explications
All the models must have :
a render method which call the render method of its view. I create a base model that I extend for the Bookshelf, Book and Page model.
a initialize method, It create the collection (books for bookshelves, pages for book...)
Finally I instanced the Bookstore model and It's done !
Code
The demonstration : http://jsfiddle.net/Atinux/DvbA3/show/
Try in the console :
Bookstore.render()
Bookstore.bookshelves.get(1).render()
Bookstore.bookshelves.get(1).books.get(2).render()
Bookstore.bookshelves.get(1).books.get(2).pages.at(0).render()
The code with comments is available here : http://jsfiddle.net/Atinux/DvbA3/
I think the best thing is to understand how the code work. Feel free to ask me if you have problems to understand perfectly my code.
The JSON data
The JSON data have to look like :
var data = {
bookshelves: [{
id: 1,
name: 'Science',
books: [{
id: 1,
name: 'Abstract Algebra',
pages: [
{ content: 'Page 1 Abstract'},
{ content: 'Page 2 Abstract'},
{ content: 'Page 3 Abstract'}
]
}, {
id: 2,
name: 'Chemistry and Technology of Fertilizers',
pages: [
{ content: 'Chemistry page 1' },
{ content: 'Chemistry page 2' },
{ content: 'Chemistry page 3' }
]
}
]
}, {
id: 2,
name: 'Psychology',
books: [{
id: 1,
name: 'How to Think Straight About Psychology',
pages: [
{ content: 'Psychology page 1' },
{ content: 'Psychology page 2' },
]
}
]
}
]
};
#Alley, what you want to do goes against the MVC pattern since your model objects must be agnostic to the view.
#Atinux answer probably works but introduces a direct dependency between your view and model objects.
All presentational methods must reside in you view objects. Communication between view and models must be done using events.
So, instead of doing: Bookstore.bookshelf.get(shelfId).render()
You shall do something along this lines: bookshelfView.render()
Related
I am trying to create a React/Redux app which lists books. Each book then has related books. I know I should structure my redux store in some sort of normalized fashion like so:
{
books: {
'book1id': {
title: 'Book 1',
author: 'Author 1',
relatedBooks: ['book2id', 'book3id']
},
'book2id': {
title: 'Book 2',
author: 'Author 2',
relatedBooks: ['book1id']
}
}
}
and load each book as necessary.
The issue is where to store loading/error data from the API requests? Some ideas I had were to create an object for every book such as
books: {
'book1id': {
isLoading: false,
error: null,
book: {
title: 'Book 1',
...
}
},
...
}
But that seems to detract slightly from the readability/intuitiveness of the state. Is there a better way to structure the state?
I structure my redux store so that it includes an entities object for all of my relational data and I store things specific to a page or a set of routes in separate parts of the state. My state tree might look something like this:
const state = {
entities: {
books: {
...
},
authors: {
...
},
...
},
booksPage: {
isLoading: false
}
}
Now I am keeping track of my relational state separate from the state of my UI components. I would not recommend trying to store a isLoading state in an individual entity since that entity may or may not exist. If you need more granular loading/error state on a per entity basis then rather on a set of entities you have a few options. The first option is keep a set of IDs that are currently loading. This is not a very practical solution because you can't track success or failure with an ID simply being in a set or not.
The next, better solution is to keep a map from ID to a status object that includes if an individual entity is loading, if it was successful or if it failed to load and what the errors were.
const state = {
entities: {
books: {
...
},
authors: {
...
},
...
},
booksPage: {
loading: {
book1: {
status: 'FAILED',
error: 'Network request failed.'
},
book2: {
status: 'SUCCESS',
},
book3: {,
status: 'IN_PROGRESS'
}
}
}
}
In summary, I find separating out your relational state into an entities child object while having page specific sections of state to be working quite well for me.
I am currently playing around with a bunch of new technology of Facebook.
I have a little problem with GraphQL schemas.
I have this model of an object:
{
id: '1',
participants: ['A', 'B'],
messages: [
{
content: 'Hi there',
sender: 'A'
},
{
content: 'Hey! How are you doing?',
sender: 'B'
},
{
content: 'Pretty good and you?',
sender: 'A'
},
];
}
Now I want to create a GraphQL model for this. I did this:
var theadType = new GraphQLObjectType({
name: 'Thread',
description: 'A Thread',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLString),
description: 'id of the thread'
},
participants: {
type: new GraphQLList(GraphQLString),
description: 'Participants of thread'
},
messages: {
type: new GraphQLList(),
description: 'Messages in thread'
}
})
});
I know there are more elegant ways to structure the data in the first place. But for the sake of experimenting, I wanted to try it like this.
Everything works fine, besides my messages array, since I do not specify the Array type. I have to specify what kind of data goes into that array. But since it is an custom object, I don't know what to pass into the GraphQLList().
Any idea how to resolve this besides creating an own type for messages?
You can define your own custom messageType the same way you defined theadType, and then you do new GraphQLList(messageType) to specify the type of your list of messages.
I don't think you can do this in GraphQL. Think that it's a bit against GraphQL philosophy of asking for the fields "you need" in each component against asking for "them all".
When the app scales, your approach will provoque higher loads of data. I know that for the purpose of testing the library looks a bit too much but it seems this is how it is designed. Types allowed in current GraphQL library (0.2.6) are:
GraphQLSchema
GraphQLScalarType
GraphQLObjectType
GraphQLInterfaceType
GraphQLUnionType
GraphQLEnumType
GraphQLInputObjectType
GraphQLList
GraphQLNonNull
GraphQLInt
GraphQLFloat
GraphQLString
GraphQLBoolean
GraphQLID
I am really stuck with tons of problems caused by Ember-data and it lacks of embedded records support.
I have searched the entire web, most of the posts are outdated others are outdated + requires me to use 3rd party libraries or to wire up 300 lines of special code with lots of drawbacks.
I've no idea how to use embedded records with ember-data as it stands today?
edit: there is a new documentation now http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html
Using the ActiveModelSerializer you can include the EmbeddedRecordsMixin which allows you to use embedded records. (In the canary versions, 1.0 beta 9+, you can use the JsonSerializer/RESTSerializer as well)
Serializer
App.ColorSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
foos: {embedded: 'always'}
}
});
Models
App.Color = DS.Model.extend({
color: DS.attr(),
foos: DS.hasMany('foo')
});
App.Foo = DS.Model.extend({
name: DS.attr()
});
JSON
{
colors:[
{
id: 1,
color: "red",
foos:[
{
id:1,
name:'something 1'
},
{
id:2,
name:'something 2'
}
]
},
...
http://emberjs.jsbin.com/qagalabaso/1/edit
For the RESTSerializer and JsonSerializer it follows the same pattern
App.ColorSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
foos: {embedded: 'always'}
}
});
http://emberjs.jsbin.com/lesiwebobi/1/edit
I would like to send data with setActiveItem() when doing view change from this store:
Ext.define('SkSe.store.Places',{
extend:'Ext.data.Store',
config:{
autoDestroy: true,
model:'SkSe.model.Places',
//hardcoded data
data: [
{
name: 'Caffe Bar XS', //naziv objekta
icon: 'Icon.png', //tu bi trebala ići ikona kategorije kojoj pripada
stamps: 'stamps1' //broj "stampova" koje je korisnik prikupio
},
{
name: 'Caffe Bar Mali medo',
icon: 'Icon.png',
stamps: 'stamps2'
},
{
name: 'Caffe Bar VIP',
icon: 'Icon.png',
stamps: 'stamps3'
}
]
//dynamic data (Matija)
//remember to change the icon path in "Akcije.js -> itemTpl"
/*proxy:{
type:'ajax',
url:'https://maps.googleapis.com/maps/api/place/search/json?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=AIzaSyCFWZSKDslql5GZR0OJlVcgoQJP1UKgZ5U',
reader:{
type:'json',
//name of array where the results are stored
rootProperty:'results'
}
}*/
}
});
This is my my details controller that should get data from places store:
Ext.define('SkSe.controller.Details', {
extend: 'Ext.app.Controller',
config: {
refs: {
placesContainer:'placesContainer',
Details: 'details'
},
control: {
//get me the list inside the places which is inside placesContainer
'placesContainer places list':{
itemsingletap:'onItemTap'
//itemtap:'onItemTap'
}
}
},
onItemTap:function(list,index,target,record){
console.log('omg');
var addcontact= Ext.create('SkSe.view.Details',
{
xtype:'details',
title:record.data.name,
data:record.data
});
var panelsArray = Ext.ComponentQuery.query('details');
console.log(panelsArray);
Ext.Viewport.add(addcontact);
addcontact.update(record.data.name);
Ext.Viewport.setActiveItem(addcontact);
console.log(record.data.name);
}
});
I would like to send name record from the places.js model above. I heard that you can't pass data with setActiveItem but should create a function that updates view(No I can't use pop and push here).
I'm not very familiar with sencha touch syntax and I'm not sure what functions to use to do that, clearly update function is not that.
I switched up your logic slightly but have provided a working example of what I think you are trying to do.
SenchaFiddle is a little different from what I get running on my machine but you should be able to get the idea.
The initial list loaded gets the data from your store, and on itemtap will push a new view onto the viewport.
You will need to add a navigation button to get back to the list from view.View as well as a better layout for the details. I would suggest nested containers or panels with custom styles to get the details just right. Alternatively you can just drop a full html snippet in there with all the data laid out like you want.
Would be happy to help more.
Fiddle: http://www.senchafiddle.com/#XQNA8
I'm developing my first EmberJS app after following some tutorials as practice. It simply contains a list of 'tables', 'columns', and 'rows' similar to a database.
Link to the problematic page: http://www.kangarooelectronics.com/fakeDB/#/tables/edit/2
My issue is that when I go to remove a column I get:
Object # has no method 'deleteRecord'
As I understand this is due to the object I'm iterating through having no references to the controller because of the way I am constructing the array that I use to create my list.
Removing tables works fine, which are listed in the following fashion:
{{#each model itemController='TableList'}}
<a {{action removeTable this}}>Delete</a>
{{/each}}
I'm iterating through the columns via:
{{#each column in currentColumns itemController='TablesEdit'}}
<a {{action removeColumn column}}>Drop</a>
{{/each}}
Snippet from FIXTURES object:
FakeDB.Table.FIXTURES = [
{
id: 1,
name: 'Users',
columns: {
1:{'colId':1, 'name':'name'},
2:{'colId':2, 'name':'favorite color'},
3:{'colId':3, 'name':'phone number'}
},
// ...snip... //
I am getting 'currentColumns' via:
FakeDB.Table = DS.Model.extend({
name: DS.attr('string'),
columns: DS.attr('object'),
rows: DS.attr('object'),
currentColumns: function() {
var newColumns = $.map(this.get('columns'), function(k, v) {
return [k];
});
return newColumns;
}.property('columns'),
// ..snip.. //
Here you can see my problem... it's obvious that my 'column' isn't going to have any methods from my controller. I tried something like this:
FakeDB.Adapter = DS.FixtureAdapter.extend();
FakeDB.Adapter.map('FakeDB.Table', {
columns: {embedded: 'load'},
rows: {embedded: 'load'}
});
FakeDB.Columns = DS.Model.extend({
colId: DS.attr('integer'),
name: DS.attr('string')
});
FakeDB.Rows = DS.Model.extend({
colId: DS.attr('integer'),
name: DS.attr('string')
});
But I couldn't get {{#each column in columns}} to work with that.
Any suggestions? I'm going to read the docs again and will post back if I find a solution.
Thanks!
EDIT:
So I think I found another solution, but I'm still running into a little issue.
FakeDB.Table = DS.Model.extend({
name: DS.attr('string'),
columns: FakeDB.Columns.find().filter(function(item, index, self) {
if(item.tableID == 1) { return true; }
})
});
Still not sure what to replace 'item.tableID == 1' with so that I get items with the tableID referencing to the current page...
Columns are structured as...
FakeDB.Columns.FIXTURES = [
{
id: 1,
tableID: 1,
name: 'name'
},
// ...snip... //
But now I get:
assertion failed: Your application does not have a 'Store' property defined. Attempts to call 'find' on model classes will fail. Please provide one as with 'YourAppName.Store = DS.Store.extend()'
I am in fact defining a 'Store' property...
I'm developing my first EmberJS app after following some tutorials as practice. It simply contains a list of 'tables', 'columns', and 'rows' similar to a database.
Most databases do contain a list of tables, rows and columns. Most web applications contain a fixed set of tables with pre-defined columns and a dynamic list of rows. If this is your first ember app i would recommend starting with something that keeps you on the happy path.
I am in fact defining a 'Store' property...
True but ember is complaining because store is not available before ember app is initialized. Anything that accesses the store should be in a framework hook of some kind. It can't be used when defining your objects, which wouldn't make a lot of sense anyway.
Probably what you meant to do was make a computed property called columns like this:
FakeDB.Table = DS.Model.extend({
name: DS.attr('string'),
columns: function() {
FakeDB.Columns.find().filter(function(item, index, self) {
if(item.tableID == 1) { return true; }
})
}.property('')
});