Accessing properties of ember-data through relationship (Not in the template) - properties

I want to stress that this problem only occurs outside of a template, such as when I try to access properties of related objects while in a controller, unit test, etc. Rendering the template seem to get the property well and work as expected.
Here is a simple example in JS Bin with a failing test http://jsbin.com/ihumuk/4/edit which repros my problem. The passing test asserts that the property is accessible and rendered in the template as expected. The failing test shows that I get null when I try to access the property with get. Really nothing fancy here but I don't understand why it's returning null.
Here is the application part of the JS Bin example:
App.ApplicationRoute = Em.Route.extend({
model: function() {
return App.Foo.find();
}
});
App.Store = DS.Store.extend({
adapter: DS.FixtureAdapter.create()
});
App.Foo = DS.Model.extend({
name: DS.attr("string"),
/**
* The subject under test
*/
childName: function() {
return this.get("child.name");
}.property("child.name"),
child: DS.belongsTo("App.Bar")
});
App.Bar = DS.Model.extend({
name: DS.attr("string")
});
App.Foo.FIXTURES = [{
id: 1,
name: "Fred",
child: 3
}, {
id: 2,
name: "Barney",
child: 4
}];
App.Bar.FIXTURES = [{
id: 3,
name: "Pebbles"
}, {
id: 4,
name: "Bam Bam"
}];
This passes.
test("Child name is rendered", function() {
expect(1);
visit("/").then(function() {
ok(find("div:contains(Pebbles)").length);
});
});
This fails.
test("Child name is accessed", function() {
expect(2);
var foo = App.Foo.find(1);
equal(foo.get("childName"), "Pebbles");
equal(foo.get("child.name"), "Pebbles");
});
This has to be something simple/stupid like forgetting a character or something, but I think I've driven myself too far into frustration to think clearly for a while. Thanks in advance for any help.

You need to use the then to know when the data is loaded
asyncTest("Child name is accessed", function() {
expect(2);
// load the data from server
App.Foo.find(1).then(function(foo) {
// the child id is 3, we need to fetch the remaining data
// and this is async, because of the ajax request
foo.get("child").then(function(child) {
equal(child.get("name"), "Pebbles");
// childName call child.name, but since the
// data is loaded, isn't necessary to use a second then
equal(foo.get("childName"), "Pebbles");
start();
});
});
});
In ember data, like major of the orm's, the data is lazy loaded, for relationships. This is because, isn't needed to return all loaded object graph, let's leave the user ask for what it want, and then load.
Because some implementations are async, like: websql, indexeddb, ajax, websockets etc. The interface of ember-data is async, so you need to use the then method to know when the data is loaded or failed.
The things work in your template, because it are binding aware. Even when the change are async, it will be finished later, and the bindings will be notified and updated.
I have updated your demo, and the tests pass http://jsbin.com/eqojaj/1/edit

Related

Vuejs - update array of an object which is in an array

I'm developing a helpdesk tool in which I have a kanban view.
I previously used nested serializers in my backend and I managed to have everything working with a single query but it's not scalable (and it was ugly) so I switched to another schema :
I query my helpdesk team ('test' in the screenshot)
I query the stages of that team ('new', 'in progress')
I query tickets for each stage in stages
So when I mount my component, I do the following :
async mounted () {
if (this.helpdeskTeamId) {
await this.getTeam(this.helpdeskTeamId)
if (this.team) {
await this.getTeamStages(this.helpdeskTeamId)
if (this.stages) {
for (let stage of this.stages) {
await this.getStageTickets(stage)
}
}
}
}
},
where getTeam, getTeamStages and getStageTickets are :
async getTeam (teamId) {
this.team = await HelpdeskTeamService.getTeam(teamId)
},
async getTeamStages (teamId) {
this.stages = await HelpdeskTeamService.getTeamStages(teamId)
for (let stage of this.stages) {
this.$set(stage, 'tickets', [])
}
},
async getStageTickets (stage) {
const tickets = await HelpdeskTeamService.getTeamStageTickets(this.helpdeskTeamId, stage.id)
// tried many things here below but nothing worked.
// stage.tickets = stage.tickets.splice(0, 0, tickets)
// Even if I try to only put one :
// this.$set(this.stages[this.stages.indexOf(stage)].tickets, 0, tickets[0])
// I see it in the data but It doesn't appear in the view...
// Even replacing the whole stage with its tickets :
// stage.tickets = tickets
// this.stages.splice(this.stages.indexOf(stage), 1, stage)
},
In getTeamStages I add an attribute 'tickets' to every stage to an empty list. The problem is when I query all the tickets for every stage. I know how to insert a single object in an array with splice or how to delete one object from an array but I don't know how to assign a whole array to an attribute of an object that is in an array while triggering the Vue reactivity. Here I'd like to put all the tickets (which is a list), to stage.tickets.
Is it possible to achieve this ?
If not, what is the correct design to achieve something similar ?
Thanks in advance !
EDIT:
It turns out that there was an error generated by the template part. I didn't think it was the root cause since a part of the view was rendered. I thought that it would have prevent the whole view from being rendered if it was the case. But finally, in my template I had a part doing stage.tickets.length which was working when using a single query to populate my view. When making my API more granular and querying tickets independently from stages, there is a moment when stage has no tickets attribute until I set it manually with this.$set(stage, 'tickets', []). Because of that, the template stops rendering and raises an issue. But the ways of updating my stage.tickets would have worked without that template issue.
I could update the stages reactively. Here is my full code; I used the push method of an array object and it works:
<template>
<div>
<li v-for="item in stages" :key="item.stageId">
{{ item }}
</li>
</div>
</template>
<script>
export default {
data() {
return {
stages: [],
};
},
methods: {
async getTeamStages() {
this.stages = [{ stageId: 1 }, { stageId: 2 }];
for (let stage of this.stages) {
this.$set(stage, "tickets", []);
}
for (let stage of this.stages) {
await this.getStageTickets(stage);
}
},
async getStageTickets(stage) {
const tickets = ["a", "b", "c"];
for (let ticket of tickets) {
this.stages[this.stages.indexOf(stage)].tickets.push(ticket);
}
},
},
mounted() {
this.getTeamStages();
},
};
</script>
It should be noted that I used the concat method of an array object and also works:
this.stages[this.stages.indexOf(stage)].tickets = this.stages[this.stages.indexOf(stage)].tickets.concat(tickets);
I tried your approaches some of them work correctly:
NOT WORKED
this.$set(this.stages[this.stages.indexOf(stage)].tickets, tickets)
WORKED
this.$set(this.stages[this.stages.indexOf(stage)].tickets, 0, tickets[0]);
WORKED
stage.tickets = tickets
this.stages.splice(this.stages.indexOf(stage), 1, stage)
I'm sure it is XY problem..
A possible solution would be to watch the selected team and load the values from there. You seem to be loading everything from the mounted() hook, and I suspect this won't actually load all the content on demand as you'd expect.
I managed to make it work here without needing to resort to $set magic, just the pure old traditional vue magic. Vue will notice the properties of new objects and automatically make then reactive, so if you assign to them later, everything will respond accordingly.
My setup was something like this (showing just the relevant parts) -- typing from memory here, beware of typos:
data(){
teams: [],
teamId: null,
team: null
},
watch:{
teamId(v){
this.refreshTeam(v)
}
},
methods: {
async refreshTeam(id){
let team = await fetchTeam(id)
if(!team) return
//here, vue will auomaticlly make this.team.stages reactive
this.team = {stages:[], ...team}
let stages = await fetchStages(team.id)
if(!stages) return
//since this.team.stages is reactive, vue will update reactivelly
//turning the {tickets} property of each stage reactive also
this.team.stages = stages.map(v => ({tickets:[], ...v}))
for(let stage of this.team.stages){
let tickets = await fetchTickets(stage.id)
if(!tickets) continue
//since tickets is reactive, vue will update it accordingly
stage.tickets = tickets
}
}
},
async mounted(){
this.teams = fetchTeams()
}
Notice that my 'fetchXXX' methods would just return the data retrieved from the server, without trying to actually set the component data
Edit: typos

Ext JS 4: Getters and setters in view

I've been thinking about this problem for a while, and I can't seem to come up with a reasonable solution. What I would like to do is create getters/setters for a textfield/its value in my view. I realize that the preferred Ext JS way is using a reference within the controller and getting it that way, but that doesn't feel very object-oriented to me. I'd also have to wrap these getters and setters because I want to output a message if the getter returns undefined. What I'd like to do is create my own getters/setters or somehow override the default getters/setters. Here are some ways I was thinking of accomplishing this.
I was thinking I could use the config {}, but that appears to only work for variables I want to define. I then was thinking of using an id somehow, but the community seems split on whether that's a good practice or not. Which leads to my current solution... wrapping. Here's my code:
LoginWindow
Ext.define('MyApp.view.LoginWindow', {
extend: 'Ext.window.Window',
alias: 'widget.loginWindow',
autoShow: true,
closable: false,
border: 0,
plain: true,
allowBlank: false,
title: "Enter your username",
modal: true,
config: {
buttons: [{
text: "Ok"
}],
items: [{
xtype: 'textfield',
fieldLabel: 'Username',
id: 'loginUserInput',
name: 'loginUserInput',
msgTarget: 'under',
validator: function(value) {
if (Ext.isEmpty(value)) {
return "You need to enter a username.";
}
return true;
}
}]
},
constructor: function(config) {
this.callParent(config);
},
getButton: function() {
console.log('here');
}
});
MyController
Ext.define('MyApp.controller.Chat', {
extend: 'Ext.app.Controller',
requires: [
'Views.ChatModule.view.LoginWindow'
],
refs: [{
ref: 'loginWindow',
selector: 'loginWindow',
xtype: 'loginWindow',
autoCreate: true
}, {
ref: 'loginUserInput',
selector: '#loginUserInput'
}],
init: function() {
// The events controller oversees
this.control({
'loginWindow button[text="Ok"]': {
'click': this.onSubmitLoginWindow
}
});
},
getLoginUserInputValue: function() {
var loginUserInput = this.getLoginUserInput();
if (loginUserInput) {
var username = loginUserInput.getValue();
if (username) {
console.log(username);
} else {
console.warn("username is undefined");
}
}
console.warn("loginUserInput is undefined");
},
onSubmitLoginWindow: function(button, event, eOpts) {
this.getLoginUserInputValue();
}
});
This works, and I realize it's a very nit-picky thing, but it just doesn't feel right to have the getter in the controller. I feel like it'd be more object-oriented if it was in the Window. However, if I put it in the Window, I believe my only option is to lean on ids or manually create the textfield in the Window's initComponent--which would involve saving off a reference of the textfield in there, but that seems a bit inefficient... as I would have to make a call to doLayout as well.
Just to reiterate, I'd love to have the getters/setters in the Window, and I'm looking for a quick way to reference it, similar to how the controller references objects. I believe the main answer will be to use ids and making a call to Ext.ComponentQuery.query('#loginUserInput') in the Window, but I'd like to know if there were any better approaches out there... like overriding the auto generated getters/setters or adding a simple getter/setter for an input's value.
Cross-post from the Sencha forums.
Edit
I guess I was a bit unclear with what I want. As a more general statement, instead of jamming all things related to my view in the controller, I'd like to store it all in the view itself, which includes things like getters/setters. One of these getters/setters just so happens to be the loginUserInput getter.
Using a model is an interesting idea, but I feel like that would be a whole lot of overhead for singleton values. I'm basically looking for something like Java's setters/getters in the LoginWindow view... and hopefully something as simple as (or close to) Java's.
The idea of including (encapsulating) it in the view makes the controller a bit cleaner, and if I delete the view, I'm deleting its functions as well, so I don't have to go hunting for the functions in the controller... all I have to worry about is removing the references (which should be minimal).
I think that the "OO" way that you're looking is to work with a Ext.data.Model for your form. If you look at the Ext.form.Basic you have methods to manipulate a model (called record) and also get the object with the values of your view. So you need:
When you create your form, use loadRecord() to bind your form to a Model.
At any time you need, use getValues() to retrieve the values of your form fields.
When submiting your form, use getRecord() and getValues() to sync your record.
Ext.define('MyApp.model.Login',{
fields : [{
name: 'username',
type: 'string'
},{
name: 'password',
type: 'string'
}]
});
Ext.define('MyApp.controller.Login',{
...
refs : [{
selector: 'window form',
ref: 'formPanel'
}],
...
openForm : function() {
//load your form and then bind the new record
var formPanel = this.getFormPanel(), //Ext.form.Panel
form = formPanel.getForm(); //Ext.form.Basic
form.loadRecord(Ext.create('MyApp.model.Login'));
},
save : function() {
//get the values in the view
var form = this.getFormPanel().getForm(),
vals = form.getValues(),
record = form.getRecord();
console.log(vals); //see the object representation of your view here
record.set(vals); //update your model
//do whatever you need with your model
}
...
});
This is an good example when you need to save the form data. In the login I think you can work directly with getValues() without binding it to a Ext.data.Model.
I am not quite certain what problem you are trying to solve to be honest with you.
If you do not like controllers listening to buttons within your window, you can have button handlers witin your view definition fire custom events that controllers can listen on. Use fireEvent method. And by the way initConfig is a recommended way to setup your views. You can break it up into methods if you wish, 'this' reference is available and is the View component being instantiated.
If you need to find inner components within the View there are many methods available from up /down to nextSibling and query .
For Components:
• Ext.getCmp(id)
• Ext.ComponentQuery.query()
• up()
• down()
• nextSibling()
• previousSibling()
• child()
• previousNode()
plus various find.. Methods
EDIT
I think I understood what you mean by getter and setters. Ext forms have the fields finders to make it easy to get and set data to individual fields. See these SO questions: Best way to access adjacent components / fields and EXT.JS getting a list of items from a fieldset
Also like Sergio said there is getRecord getValues and setRecord methods on the form to deal with data binding. Thats it.
EDIT2
The best starting point guide that shows clear and claen MVC patterns as well as form handling. http://docs.sencha.com/extjs/4.1.3/#!/guide/application_architecture
My thoughts are something like this:
...
items: [],
constructor: function(config) {
this.loginUserInput = Ext.create('Ext.form.field.Text', {
fieldLabel: 'Username',
id: 'loginUserInput',
name: 'loginUserInput',
msgTarget: 'under',
validator: function(value) {
if (Ext.isEmpty(value)) {
return "You need to enter a username.";
}
return true;
}
});
this.items.push(this.loginUserInput);
this.callParent(config);
},
getLoginUserInput: function() {
var loginUserInput = this.loginUserInput;
if (!loginUserInput) {
console.warn("LoginWindow::getLoginUserInput: loginUserInput is undefined");
}
return loginUserInput;
}
So instead of letting Ext do its magic, I am now instantiating the object on my own, which then allows me to store away a reference of it, so I can easily access it in my getter. I just wonder if this is creating any sort of performance hit. It doesn't seem like it'd be that much worse... it actually seems like it'd be a bit better because I'm not referencing this object by its ID, and I don't have to go searching for it when I need it.

Profile feature in Sencha Touch 2 causes problems in production mode build

I have created a Sencha Touch 2 app and built a production mode version. However, I have encountered a big issue with the production build and it running in Phone/Tablet modes.
The current profile implementation of ST2 seems flawed as even if you have a specific profile activated, all views are still loaded in. In my application I want to be able to specify views using the xtype alias in the view config, and have the correct view for phone or tablet profile loaded in without any special coding. If all views from profiles are loaded in then this can't work (one view will always override another).
The only way I could achieve this was to dynamically add the profile at bootup stage (within app.js) like so:
Ext.application({
name: 'MyTestApp',
var activeProfile = Ext.os.is.Phone ? ['Phone'] : ['Tablet'];
requires: [ ... ],
profiles: activeProfile
});
This has worked fine. It means I can then load the correct view and still just use the xtype alias within the config of another view and/or ref in a controller. However, I noticed that when I generate a production build and load up a console window, both of the following are defined:
MyTestApp.views.phone.Login
MyTestApp.views.tablet.Login
Normally the tablet or phone version would be undefined depending on the profile. I'm assuming this is the case because the production mode build has parsed ALL dependencies and then included all views regardless of the profile.
So in my start-up controller I have a button handler which then creates a login view from the xtype.
Controller:
refs: {
loginView: {
selector: 'loginview',
xtype: 'loginview',
autoCreate: true
}
}
Handler:
var loginView = this.getLoginView();
In development mode, the loginView variable will either be MyTestApp.views.tablet.Login or MyTestApp.views.phone.Login depending on the profile.
How do I ensure that the loginview instantiated here gets the correct version depending on the profile when in production mode?
I had been struggling with this, when I would move either of the solutions to the devices, I would be stuck with the fact that all views are referenced and would get some xtype collision always giving me the phone view. ( i had to move to aliases eventually - not sure why :( ). I finally managed to crack this for my use case, just sharing for future reference.
I am running touch 2.3.1 and cordova 3.3.1 with the latest cmd 4.0.2.67
I use the solution from Christopher except I had to change the source code in the sencha touch source directory rather than keep it in the app.js [truthfully I don't know why it hangs when I leave it as an override]
In addition I have had to configure the views the following way in order for:
define a base class for the view with an alias so the controller to understand the ref as it loads first
dynamically assign the alias to the view instantiated by the profile
strip out (using Christopher code)
Base class for the views
Ext.define('MyApp.view.CatalogView', {
extend: 'Ext.Container',
alias: 'widget.catalogview'
});
Assign an alias to the profile specific view
Ext.define('MyApp.profile.Phone', {
extend: 'Ext.app.Profile',
config: {
name: 'Phone',
views: ['CatalogView'],
},
isActive: function() {
return Ext.os.is('Phone');
},
launch: function() {
Ext.ClassManager.setAlias('MyApp.view.phone.CatalogView', 'widget.catalogview');
}
});
Repeat for the tablet view
For all who want to know how I resolved this, I'm now left bald after pulling all my hair out;)
All my profile views where I want to have the xtype names remain the same even though they might belong in the phone or tablet profiles, I have to remove the alias/xtype config on the class. I then have a profile base class defined like so with a shared helper function:
Ext.define('MyApp.profile.Base', {
extend: 'Ext.app.Profile',
config: {
},
mapViewAliases: function () {
var self = this;
var views = this.getDependencies().view;
var newAliasMap = null;
Ext.each(views, function (view) {
Ext.Array.some(self.getViewsToAliasMap(), function (map) {
if (map[view]) {
if (!newAliasMap) {
newAliasMap = {};
}
newAliasMap[view] = [map[view]];
return true;
}
});
});
if (newAliasMap) {
console.log('view aliases being mapped for: ' + this.$className);
Ext.ClassManager.addNameAliasMappings(newAliasMap)
}
}
});
Then I have the profile class inherit from the base class (this is repeated with the tablet profile except the viewsToAliasMap holds classes belonging to the tablet profile instead of the phone profile):
Ext.define('MyApp.profile.Phone', {
extend: 'MyApp.profile.Base',
config: {
name: 'Phone',
views: ['Login', 'Home', 'Welcome' ],
viewsToAliasMap: [
{ 'MyApp.view.phone.Login': 'widget.loginview' },
{ 'MyApp.view.phone.Home': 'widget.homeview' },
{ 'MyApp.view.phone.Welcome': 'widget.welcomeview' }
]
},
isActive: function () {
return Ext.os.is.Phone;
},
launch: function () {
console.log("Phone profile launched");
this.mapViewAliases();
}
});
So basically, the profile calls the function mapViewAliases() on the base class in the launch function. The mapViewAliases() registers the view class names with the aliases defined in the profile with the class manager. So effectively the xtype names are resolved at run-time.
I'm sure this code can be improved and/or a better way to do this.
Please feel free to let me know.
I am using a pretty naive implementation... I'm sure it could be made more robust, but I've been hacking at this for 5 hours or so now.
Ext.define('MyApp.override.Application', {
override : 'Ext.app.Application',
onProfilesLoaded: function() {
var profiles = this.getProfiles(),
length = profiles.length,
instances = [],
requires = this.gatherDependencies(),
current, i, profileDeps;
for (i = 0; i < length; i++) {
var instance = Ext.create(profiles[i], {
application: this
});
/*
* Note that we actually require all of the dependencies for all Profiles - this is so that we can produce
* a single build file that will work on all defined Profiles. Although the other classes will be loaded,
* the correct Profile will still be identified and the other classes ignored. While this feels somewhat
* inefficient, the majority of the bulk of an application is likely to be the framework itself. The bigger
* the app though, the bigger the effect of this inefficiency so ideally we will create a way to create and
* load Profile-specific builds in a future release.
*
CMK - PSHAW!
*/
if (instance.isActive() && !current) {
console.log('Profile active: ' + instance.getName());
current = instance;
profileDeps = instance.getDependencies();
requires = requires.concat(profileDeps.all);
var ns = instance.getNamespace();
this.setCurrentProfile(current);
// Merge Controllers, Models, Stores, and Views
this.setControllers(this.getControllers().concat(profileDeps.controller));
this.setModels(this.getModels().concat(profileDeps.model));
this.setStores(this.getStores().concat(profileDeps.store));
this.setViews(this.getViews().concat(profileDeps.view));
// Remove the view ref and requires for default views, when a profile specific one exists
Ext.each(profileDeps.view, function(className) {
if (className.indexOf('view.' + ns + '.') !== -1) {
// Requires
var index = requires.indexOf(className.replace('view.' + ns, 'view'));
if (index !== -1) {
requires.splice(index, 1);
}
// Views
index = this.getViews().indexOf(className.replace('view.' + ns, 'view'));
if (index !== -1) {
this.getViews().splice(index, 1);
}
}
}, this);
instances[0] = instance;
break;
}
}
this.setProfileInstances(instances);
Ext.require(requires, this.loadControllerDependencies, this);
}
});
Put this before your Ext.application, and it replaces the profile loader... This one strips out default views with the same name as one in the active profile namespace.
It requires that you define an xtype for the views that match, then even your refs in controllers will work...
I need to continue testing with this, but it looks promising so far.

WinJS Listview shows undefined when navigating quickly

I have a WinJS application with listviews in which if quickly navigate between pages before the listview is fully loaded, the next page shows the listview with all elements in it bound as "undefined".
So say I have a hub page with a "to do" that is filtered to only show 6 items, and there is a header that navigates to the full "to do" page, when the hub page is displayed but before it is fully loaded I click on the header link to the "to do" page, the app then goes to the "to do" page, but the items show up with all the properties in the tile as "undefined".
I am using IndexedDB as my data store.
My home page code looks like this:
WinJS.UI.Pages.define("/pages/home/home.html", {
ready: function (element, options) {
WinJS.Utilities.query("a").listen("click", function (e) {
e.preventDefault();
WinJS.Navigation.navigate(e.currentTarget.href);
}, false);
viewModel = new HomeViewModel(element);
viewModel.load(); //loads from indexed db
},
//etc...
To Do Page:
WinJS.UI.Pages.define("/pages/ToDo/ToDo.html", {
ready: function (element, options) {
viewModel = new ToDoViewModel(element);
viewModel.load();
},
etc//
I know there isn't much to go off, but any ideas would be appreciated.
Also tips on how to debug something like this would be great.
Update
I narrowed it down to this one line from the Hub Page:
myLib.GetData(todaysDate, function (result) {
that.trendsModel.today = result;
WinJS.Binding.processAll(that.el.querySelector("#dataPanel"), that.trendsModel); //<--Right Here
});
If I remove that, then when I load the second page the data doesn't show as undefined. What is interesting is the data initially shows correctly on the second page and then it changes to "undefined".
Solution
My fix:
myLib.GetData(todaysDate, function (result) {
var element = that.el.querySelector("#dataPanel");
that.trendsModel.today = result;
if(element) {
WinJS.Binding.processAll(element, that.trendsModel);
}
});
At the point when when the callback returns, I am already on the second page. So the selector was not found returning null. If you pass null to processAll it tries to bind the whole page which is why I was able to see the correct data for a second then it changes to undefined...Wow, what a doozy. I guess it makes sense but what a pain to find.
Hope it helps someone in the future :)
Your ToDoViewModel, and HomeViewModel need to be observable. This means they need to mix in from WinJS.Binding.mixin, and for the properties that you pull in asynchronously, they need to call this.notify("propertyName", newVal, oldVal) from the property setter.
Note that you need to have getter/setter properties. e.g.
var bindingBase = WinJS.Class.mix(function() {}, WinJS.Binding.mixin);
WinJS.Namespace.define("YourNamespace", {
ToDoViewModel: WinJS.Class.derive(bindingBase, function constructor() {
}, {
_titleStorage: "",
title: {
get: function() { return this._titleStorage; },
set: function(newValue) {
if(newValue === this._titleStorage) {
return;
}
var old = this._titleStorage;
this._titleStorage = newValue;
this.notify("title", newValue, old);
}
}
}),
});
myLib.GetData(todaysDate, function (result) {
var element = that.el.querySelector("#dataPanel");
that.trendsModel.today = result;
if(element) {
WinJS.Binding.processAll(element, that.trendsModel);
}
});
At the point when when the callback returns, I am already on the second page. So the selector was not found returning null. If you pass null to processAll it tries to bind the whole page which is why I was able to see the correct data for a second then it change to undefined...Wow, what doozy. I guess it makes sense but what a pain to find.

view is undefine, loading combo box issue in 4.07

I am occassionally ( usually 1 in 3 page loads) receive the following error message
view is undefined
view.onItemSelect(record);
In my view
{
xtype:'combobox',
name:'PurchaseOrderStatusId',
id:'PurchaseOrderStatusCombo',
displayField:'Name',
store:'PurchaseOrderStatuses',
mode:'local',
valueField:'Id',
fieldLabel:'Status',
width: 350
},
{
xtype:'combobox',
name:'SupplierId',
id:'SupplierCombo',
displayField:'Name',
store:'Suppliers',
mode:'local',
valueField:'Id',
fieldLabel:'Suppliers',
width: 350
},
// in my controller
onLaunch: function () {
var suppliers = this.getSuppliersStore();
suppliers.load();
var purchaseOrderStatuses = this.getPurchaseOrderStatusesStore();
purchaseOrderStatuses.load();
var purchaseOrdersStore = this.getPurchaseOrdersStore();
purchaseOrdersStore.load({
callback: this.onPurchaseOrderLoad,
scope: this
});
},
onPurchaseOrderLoad: function (selection) {
var form = Ext.getCmp('purchaseOrderForm');
form.loadRecord(selection[0]);
},
in my model
{
mapping:'PurchaseOrderStatusId',
name:'PurchaseOrderStatusId'
},
{
mapping:'SupplierId',
name:'SupplierId'
}
It's not clear how the error message you are reporting is linked to any of the code you have revealed so far. So it's difficult to connect the dots here.
However here are a couple of observations:
1. If you need your stores to always load ASAP when your app launches set the autoLoad:true config on those stores. This way you don't have to explicitly load them and they have more time to finish loading before your view is ready to go.
2. If you need to load an instance of your model into the form you can use Model.load method instead of having a store do that for you. You will need to provide API on the model on how to read the records from the server.
3. Model mapping is not necessary if your Model fields match to what the server returns.
If you need further help, please update your question with some more debug info.