I will try to be as detailed as possible, it is a complicated question.
This part works perfect but please allow me to explain. I have a template which I use for many controllers:
<script type='text/x-handlebars' data-template-name='main-content'>
... bunch of components ...
<!-- background 100% x 100% -->
<div id="content">
<div id="svg_cont" style="position:relative; top:0; left:0; width:100%;height:100%;">
<svg id="svg" class="svg-main" width="100%" height="100%" {{bind-attr viewBox="pageViewbox"}} preserveAspectRatio="xMidYMid meet">
<g id="svg-view" class="svg-view">
<path {{bind-attr d="pageBoundaryPath"}} class="svg-drawing-boundary" />
{{#each}}
{{#if this.isShape}}
{{view App.ShapeView content=this id=this.guid}}
{{/if}}
{{/each}}
</g>
</svg>
</div>
</div>
... couple other components ...
</script>
Please note the template contains inline SVG in the HTML and this is where my fixtures are being rendered inside of the #each loop. I pre-loaded 4-5 fixtures when I made the model shown below - when the page initially loads this all works and looks perfect:
App.Shape = App.Asset.extend({
... many properties but irrelevant .....
shapeTag: DS.attr('string', {defaultValue: "path"}),
d: DS.attr('string', {defaultValue: ""}),
svgAttributes: DS.attr(),
init: function() {
this._super();
},
didLoad: function() {
this._super();
}
});
which is based off of a base model:
App.Asset = DS.Model.extend({
guid: DS.attr('string'), // REQUIRED, unique id
type: DS.attr('string'), // REQUIRED, shape | stencil
isShape: function(){
return this.get("type") === "shape";
}.property("type"),
rootTagId: DS.attr('string', {defaultValue: "svg-view"}), //?? necessary??
// what to surround the true object with
parentTag: DS.attr('string', {defaultValue: "g"}),
});
and pre-loaded with some shape objects:
App.Shape.FIXTURES = [{
"id": "line124",
"guid": "line124",
"type": "shape",
"d": "M57,57L510,57L510,510L57,510,L57,57",
"svgAttributes": {"stroke-width":"1", "stroke":"#ff9900", "fill":"none"},
"css": "idp-asset,idp-line"
},
......
];
When the page loads ALL the shapes pre-loaded in the fixtures render and look great. The backend View that is used to render them is below, please note the init method which builds a dynamic template compiled on the fly:
App.ShapeView = Ember.View.extend({
id: null,
tagName: null,
content: null,
classNames: [],
attributeBindings: ["trans:transform"],
template: function(){
var tmp = this.get("dynamicTemplate");
return Ember.Handlebars.compile(tmp);
}.property(),
didInsertElement: function(){
this._super();
},
dynamicTemplate: "",
init: function() {
this._super();
this.set("id", this.get("content.guid"));
this.set("tagName", this.get("content.parentTag"));
this.set("classNames", this.get("content.cssClasses"));
// iterate all attrs
var tmp = '';
tmp += '<' + this.get("content.shapeTag") + ' ';
tmp += ' {{bind-attr d=view.content.d}} ';
/*
this writes out dynamic 'bind-attr' template code
to bind all the SVGs element attributes to the
model.svgAttributes. object and it's properties,
they are a 1 to 1 relationship with valid svg attributes
and valid values, such as stroke-width: 1.4 OR stroke:"red"
*/
var svgAttrs = this.get("content.svgAttributes");
for (var k in svgAttrs) {
tmp += ' {{bind-attr ' + k + '=view.content.svgAttributes.' + k + '}} ';
}
tmp += '></' + this.get("content.shapeTag") + '>';
this.set("dynamicTemplate", tmp);
}
});
NOW TO THE PROBLEM! whew... this all works perfect, items are rendered from the fixture data, the svg objects are inserted into the DOM and they appear on screen in the browser window. I can update model properties in the store and the changes are propagated to the screen as expected. It works awesome. The problem is when I run this command in code from a component (OR ANYWHERE):
var that = this;
var newline = {
"id": "line112233",
"guid": "line112233",
"type": "shape",
"d": "",
"shapeTag": "circle",
"svgAttributes": {
"cx": "200",
"cy": "200",
"r": "200",
"fill": "red"
},
"css": "idp-asset, idp-line"
};
d3.select("#svg").on("click", function(e, i){
var s = that.store.createRecord('shape', newobj).save().then(function(obj){
console.log(obj);
});
});
Everything works perfectly as expected except the item never appears on screen. Please note, the object appears in the DOM as expected but is not displayed on screen. When viewing the site in chrome inspector I SEE THE NEW ELEMENT fine, it appears like this below:
<g id="line112233" class="idp-asset idp-line" transform="translate(0,0)scale(1)rotate(0)">
<circle d="" data-bindattr-44="44" cx="200" data-bindattr-45="45" cy="200" data-bindattr-46="46" r="200" data-bindattr-47="47" fill="red" data-`bindattr-48="48">
</circle>
</g>
It ALSO appears under the ember inspector as an added item in the data store, it appears exactly like the other fixtures that were pre-loaded do. This problem occurs if I used .push or .createRecord when adding the new item to the store. So to recap, I add the newly created Shape, and it appears in ember with correct data, it appears in the ember view in the ember inspector, it appears in the DOM when viewing it in Chrome inspector but it DOES NOT ever render on screen.
I have done everything and googled everything I can think of. I loaded the most recent code to http://v4-staging.idplans.com:8181/rpm.htm if you want to see it in action or view source. Go to menu in upper right, select "design mode", open slider on right hand side, and choose the line button, and just click in the center somewhere and you will have a new object in the store, view, and DOM but NOT ON screen.
I really appreciate any help and you reading this long :)
EDIT/UPDATED I thought this was a solution:
In the VIEW (NOT TEMPLATE) for the main outlet (in my case the base controller that my other controllers all extend) I tried this:
App.PageBaseView = Ember.View.extend({
........
// THIS IS WHAT I ADDed, BY WATCHING MY CONTENT OF THE CONTROLLER
// AND FORCING A RE-RENDER ON CURRENT (THIS) VIEW THE OBJECT IS NOW DISPLAYED
// ON SCREEN
contentChanged: function() {
this.rerender();
}.observes('controller.content.@each')
});
Because the object started appearing but because the page re-rendered I lost all event listeners and page state and properties went back to initialized values.... Still looking for answer
this.rerender()
in your view after the the operation is complete. You'll probably need some type of debounce tho. – MilkyWayJoe