Hey I'm having two different issues in my ember app, both of which involve bindings.
First, I have a binding firing when I don't want it to. Basically what I'm trying to achieve (I'm building a survey creator front-end app) is that when any text is entered into the 'name' field of a question, I want to add a new question object, which will render out another blank question at the end of the list of questions that the user is adding. This has the effect of there always being a new question, so an add question button is not required. The binding is working, and a new object is being added: however, since the binding is from the newest question object, the binding is triggered again when the new object is created, which in turn creates a new object, which triggers the binding again....which obviously eventually crashes the browser. I've tried using the Ember._suspendObserver function, but there isn't a lot of documentation on this, and I think I'm using it wrong - anyhow it isn't suspending the observer or pausing the binding. The observer in the code is around line 27 (contentsNameObserver)
The other issue I'm having -- I have a selection drop down box which selects what type of question the user wants (single answer, multi-choice, etc.) but the binding between the select box and the {{#each}} helper which renders the kind of question isn't triggering. I'm using the Ember.Select view helper, so there shouldn't be any issues with using get/set to fire the binding. I'm using a computed property to return an array of fields for the question type based on the value of the question type id. The computed property is in line 13 (App.SurveyContent.types), and the template templates/step3. Quick heads up that this app may be extended for more than surveys, hence 'questions' are often referred to in the code as 'content'.
I'm pretty new to ember (this is my first real app) so my code most likely has a lot of issues outside of these problems...so any comments on how I've structured my app would be hugely appreciated as well!
Javascript ember app:
App = Ember.Application.create({
rootElement: '#emberContainer'
});
App.SurveyContent = Ember.Object.extend({
name: "",
content_type: 1,
content_pos: 1,
hash: Em.A([]),
types: function() {
alert("redraw");
return App.ContentTypes[this.content_type-1].hash;
}.property()
});
App.Surveys = Ember.Object.create({
name: null,
start: $.datepicker.formatDate('mm/dd/yy' , new Date()),
end: $.datepicker.formatDate('mm/dd/yy' , new Date()),
themeID: 0,
contents: [App.SurveyContent.create()], //Pushing an instance of App.SurveyContent onto this
contentsNameObserver: function() {
context = this;
console.log("entering");
Em._suspendObserver(App.Surveys, "contents.lastObject.name", false, false, function() {
console.log("suspend handler");
context.contents.pushObject(App.SurveyContent.create());
})
}.observes("contents.lastObject.name")
});
App.ContentTypes = [
Ember.Object.create({name: 'Text question', id:1, hash: [Ember.Object.create({name: 'Question', help: 'Enter the question here', type: 'text'})]}),
Ember.Object.create({name: 'Multichoice question', id:2, hash: [Ember.Object.create({name: 'Question', help: 'Enter the question here', type: 'text'}),
Ember.Object.create({name: 'Answer', help: 'Enter possible answers here', type: 'text', multiple: true})]})
];
App.ViewTypeConvention = Ember.Mixin.create({
viewType: function() {
console.log(this);
return Em.get("Ember.TextField");
}.property().cacheable()
});
App.CRMData = Ember.Object.extend();
App.CRMData.reopenClass ({
crm_data: [],
org_data: [],
org_display_data: [],
loadData: function() {
context = this;
context.crm_data = [];
$.getJSON ("ajax/crm_data", function(data) {
data.forEach(function(crm) {
context.crm_data.pushObject(App.CRMData.create({id: crm.crm_id, name: crm.crm_name}));
crm.orgs.forEach(function(org) {
context.org_data.pushObject(App.CRMData.create({id: org.org_id, name: org.org_name, crm_id: crm.crm_id}));
}, context)
}, context)
context.updateOrganisations(5);
});
return this.crm_data;
},
updateOrganisations: function(crm_id) {
context = this;
this.org_display_data.clear();
console.log("clearing the buffer")
console.log(this.org_display_data)
context.org_data.forEach(function(org) {
if(org.crm_id == crm_id) {
context.org_display_data.pushObject(App.CRMData.create({id: org.id, name: org.name}));
}
}, context)
}
});
App.DateField = Ember.TextField.extend({
attributeBindings: ['id', 'class']
});
App.CRMSelect = Ember.Select.extend({
attributeBindings: ['id'],
change: function(evt) {
console.log(evt)
App.CRMData.updateOrganisations($('#crm').val())
}
});
App.ApplicationController = Ember.Controller.extend();
App.Step1Controller = Ember.ArrayController.extend({});
App.Step2Controller = Ember.ArrayController.extend({});
App.Step2Controller = Ember.ArrayController.extend({});
App.ApplicationView = Ember.View.extend({
templateName: 'app'
});
App.Step0View = Ember.View.extend ({
templateName: 'templates/step0'
});
App.Step1View = Ember.View.extend ({
templateName: 'templates/step1'
});
App.Step2View = Ember.View.extend ({
templateName: 'templates/step2',
didInsertElement: function() {
$( ".jquery-ui-datepicker" ).datepicker();
}
});
App.Step3View = Ember.View.extend ({
templateName: 'templates/step3',
});
App.Router = Em.Router.extend ({
enableLogging: true,
root: Em.Route.extend ({
showstep1: Ember.Route.transitionTo('step1'),
showstep2: Ember.Route.transitionTo('step2'),
showstep3: Ember.Route.transitionTo('step3'),
index: Ember.Route.extend({
route: '/',
connectOutlets: function(router){
router.get('applicationController').connectOutlet( 'step0');
}
}),
step1: Ember.Route.extend ({
route: 'step1',
connectOutlets: function(router){
router.get('applicationController').connectOutlet( 'step1', App.CRMData.loadData());
}
}),
step2: Ember.Route.extend ({
route: 'step2',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('step2')
},
}),
step3: Ember.Route.extend ({
route: 'step3',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('step3')
},
})
})
});
Ember.LOG_BINDINGS=true;
App.LOG_BINDINGS = true;
App.ContentTypes.forEach(function(object) {
object.hash.forEach(function(hash) {
hash.reopen(App.ViewTypeConvention);
}, this);
}, this);
Html templates (I've got these in haml, so this is just a representation of the important ones)
<script type="text/x-handlebars" data-template-name="app">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="templates/step3">
<h1> Add content to {{App.Surveys.name}} </h1>
<br>
<div id = "accordion2" class = "accordion">
{{#each content in App.Surveys.contents}}
<div class="accordion-group">
<div class = "accordion-heading">
<a class = "accordion-toggle" data-parent = "#accordion2" data-toggle = "collapse" href = "#collapseOne">
{{content.name}}
</a>
</div>
<div id = "collapseOne" class = "accordion-body collapse in">
{{view Ember.TextField valueBinding="content.name" class="txtName"}}
<form class = "form-horizontal">
<div class = "accordion-inner">
<div class = "control-group">
<label class = "control-label" for ="organisation">
Content Type
<div class = "controls">
{{view Ember.Select contentBinding="App.ContentTypes" optionValuePath="content.id" optionLabelPath="content.name" valueBinding="content.content_type"}}
</div>
</div>
</div>
{{#each item in content.types }}
<div class = "control-group" >
<label class = "control-label" for = "organisation">
{{item.name}}
<div class = "controls">
{{view item.viewType }}
</div>
{{/each}}
</div>
</form>
</div>
{{/each}}
</div>
</div>
<br>
<div class = "btn" {:_action => 'showstep3'}> Next Step > </div>
</script>