How to observe adding element to array - ember.js

I want to observe adding element to array.
below is test program.
<!-- library load -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="js/libs/jquery-1.6.1.min.js"%3E%3C/script%3E'))</script>
<script src="http://cloud.github.com/downloads/emberjs/ember.js/ember-0.9.5.min.js"></script>
<script type="text/x-handlebars">
{{#each App.ArrayController.array}}
{{foo}}
{{/each}}
<button onclick="App.ArrayController.addElement();">add</button>
</script>
<script type="text/javascript">
var App = Em.Application.create();
App.ArrayController = Em.Object.create({
array: [{foo:1}, {foo:2}, {foo:3}],
addElement: function() {
this.array.pushObject({foo:4});
},
elementAdded: function() {
alert('ok'); // not invoked...
}.observes('array')
})
</script>
But when call addElement, elementAdded is not invoked...
How do I observe adding element?

use observes('array.#each') instead. jsfiddle code is here

You can use Ember.ArrayController and overwrite the arrayDidChange function
And optionaly call other methods from ther.
<!-- library load -->
<script type="text/x-handlebars">
{{#each App.ArrayController.array}}
{{foo}}
{{/each}}
<button onclick="App.ArrayController.addElement();">add</button>
</script>
<script type="text/javascript">
var App = Em.Application.create();
App.arrayController = Ember.ArrayController.create({
content: [{foo:1}, {foo:2}, {foo:3}],
addElement: function() {
console.log(this);
var array = this.get('content')
array.pushObject({foo:4});
// this.set('array', array);
},
elementAdded: function() {
console.log('ok'); // not invoked...
}.observes('array'),
arrayDidChange: function(item, idx, removedCnt, addedCnt) {
this.elementAdded();
this._super(item, idx, removedCnt, addedCnt);
}
});
</script>
And you can use Observers

Check out this fiddle to see how to know exactly what object has been added or removed from the array using ArrayController.

Related

Ember #each content doesn't update when replacing model

I have the following index.html:
<!DOCTYPE html>
<html>
<body>
<script type="text/x-handlebars" id="index">
<ul>
{{#each todo in todos}}
<li>{{todo}}</li>
{{/each}}
</ul>
<button {{action 'generate'}}/>Generate a to-do</buton>
</script>
<script src="js/libs/jquery-1.10.2.js"></script>
<script src="js/libs/handlebars-1.1.2.js"></script>
<script src="js/libs/ember-1.6.1.js"></script>
<script src="js/app.js"></script>
</body>
</html>
And app.js:
App = Ember.Application.create();
App.Router.map(function() {});
App.IndexRoute = Ember.Route.extend({
model: function() {
return {todos: ['To-do 1', 'To-do 2']};
},
});
// This is a function I cannot change, because I don't own it.
// So I'm forced to get the updated model as the result of this.
// Here is some dummy-but-working implementation, for simulation purpose:
function generate(todolist) {
var n = todolist.todos.length + 1;
todolist.todos.push("To-do " + n);
return todolist;
}
App.IndexController = Ember.ObjectController.extend({
actions: {
generate: function() {
var oldToDoList = this.get('model');
var newToDoList = generate(oldToDoList);
this.set('model', newToDoList);
console.log(this.get('model').todos);
},
},
});
When I click on the generate button, I effectively see the growing to-dos array in console, but UI doesn't update.
Shouldn't #each content update automatically when completely replacing controller's model, or am I missing something?
your generate method doesn't actually generate a new array, so Ember won't notice that you've changed the property (because it's a reference to the same array). In your particular instance you should just use pushObject and Ember will know you're modifying the same array.
function generate(todolist) {
var n = todolist.todos.length + 1;
todolist.todos.pushObject("To-do " + n);
return todolist;
}

Ember: Context lost in ember component

I'm having a problem with Ember, although not too sure where the problem lies, whether it's within Handlebars or Ember component.
The issue is when a controller object context is passed in to an ember component, as an argument, the context is undefined. However, logging the object within the handlebars template, immediately preceding the component, shows the correct object (see index and components/component-button templates).
Templates
<script type="text/x-handlebars" data-template-name="application">
<h1>Fiddling</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
{{#each sections}}
{{#each buttons}}
{{log 'source in template' ../source}}
{{component-button source=../source options=this}}
{{/each}}
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="components/component-button">
{{log 'source in component' source}}
<button {{action 'options.action'}}>{{options.label}}</button>
</script>
Application
App = Ember.Application.create( {} );
/**
* Component: Button
*/
App.ButtonComponent = Ember.Component.extend( {
tagName: 'button',
click: function click() {
this.get( 'source' ).send( this.get( 'options.action' ) );
}
} );
/**
* Controller: Application
*/
App.IndexController = Ember.ObjectController.extend( {
sections: Ember.A(),
actions: {
doSomething: function() {
window.console.log( 'hooray!' );
}
},
setup: function setup() {
var source = this;
var section = Ember.Object.create( {
source: source,
buttons: Ember.A( [
Ember.Object.create( {
label: 'Do something!',
action: 'doSomething'
} )
] )
} );
this.sections.pushObject( section );
}.on( 'init' )
} );
App.IndexRoute = Ember.Route.extend( {
model: function() {
return { name: 'foo' };
}
} );
See fiddle
Instead of referring to ../source in the each loop, do the following:
<script type="text/x-handlebars" data-template-name="index">
{{#each section in sections}}
{{#each section.buttons}}
{{log 'source in template' section.source}}
{{component-button source=section.source options=this}}
{{/each}}
{{/each}}
</script>
In the first each 'section' is defined, which can be used in nested each statements to refer to the section.

How to use CollectionView inside View to not use the defaultContainer in Ember JS

I want to insert CollectionView into View. It works but displays:
DEPRECATION: Using the defaultContainer is no longer supported. [defaultContainer#lookup]
How correctly insert CollectionView in View?
App = Ember.Application.create();
App.Router.map(function() {
this.route("index", { path: "/" });
});
App.FirstView = Em.View.extend({
templateName: 'first'
});
App.SecondView = Em.View.extend({
templateName: 'second'
});
App.MyCollection = Em.CollectionView.extend({
content: ['f','s'],
createChildView: function(viewClass, attrs){
if (attrs.content == 'f') {
viewClass = App.FirstView ;
};
if (attrs.content == 's') {
viewClass = App.SecondView ;
};
return this._super(viewClass, attrs);
}
});
App.IndexView = Em.View.extend({
myChildView: App.MyCollection.create()
});
templates:
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
{{view view.myChildView}}
</script>
<script type="text/x-handlebars" data-template-name="first">
search
</script>
<script type="text/x-handlebars" data-template-name="second">
client
</script>
Sorry for my english, i am from Russia and understand it a little))
Your IndexView is directly instantiating the view. This style of instantiation is deprecated with nested views, so as to allow child views to get their parent Container.
Change that to declare the myChildView directly, and the deprecation warning will go away.
App.IndexView = Em.View.extend({
myChildView: App.MyCollection
});

How to detect if a view is ready?

I have the following html and js code snippets. Basically, I'm trying out Ember's select element. The problem is that I can't detect when the select element is ready to access.
HTML:
<!DOCTYPE html>
<html>
<head>
<title></title>
<link href='lib/uniform/css/uniform.default.css' rel='stylesheet'/>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="../lib/ember.min.js"></script>
<script type="text/javascript" src='lib/uniform/jquery.uniform.js'></script>
<script type="text/javascript" src="Form.js"></script>
</head>
<body>
<script type="text/x-handlebars">
</script>
<script type="text/x-handlebars">
{{#view contentBinding="FormExample.selectValues" valueBinding="type" tagName="select"}}
{{#each content}}
<option {{bindAttr value="fullName"}}>{{fullName}}</option>
{{/each}}
{{/view}}
</script>
</body>
</html>
JS:
FormExample = Ember.Application.create({
ready: function()
{
this._super();
// $("select").uniform(); // doesn't work
$(document).ready( function(){
console.log( $("select") );
//$("select").uniform(); // doesn't work
});
}
});
FormExample.Person = Ember.Object.extend({
id: null,
firstName: null,
lastName: null,
fullName: function()
{
return this.get('firstName') + " " + this.get('lastName');
}.property('firstName','lastName').cacheable()
})
FormExample.selectValues = Ember.ArrayController.create({
content: [
FormExample.Person.create({id:1, firstName: 'a', lastName:'a'}),
FormExample.Person.create({id:2, firstName: 'b', lastName:'b'}),
FormExample.Person.create({id:3, firstName: 'c', lastName:'c'})
],
// test for auto binding
add: function()
{
this.pushObject( FormExample.Person.create({id:4, firstName: 'd', lastName: 'd'}) );
}
});
Output: []
I found it..
Changes to HTML:
instead of using view and create option manually, use the following code
{{view FormExample.select
contentBinding="FormExample.selectOptions"
selectionBinding="FormExample.selectedOption.person"
optionLabelPath="content.fullName"
optionValuePath="content.id"}}
Changes to JS:
FormExample.select = Ember.Select.extend({
didInsertElement: function()
{
$("select").uniform();
}
});

Trigger an action in a text field from a button

I'm looking for advice on how to trigger this view function insertNewLine from a button (see view and template below). I'm guessing there's probably a better way to structure this code. Thanks for your help.
// view
App.SearchView = Ember.TextField.extend({
insertNewline: function() {
var value = this.get('value');
if (value) {
App.productsController.search(value);
}
}
});
// template
<script type="text/x-handlebars">
{{view App.SearchView placeholder="search"}}
<button id="search-button" class="btn primary">Search</button>
</script>
You could use the mixin Ember.TargetActionSupport on your TextField and execute triggerAction() when insertNewline is invoked. See http://jsfiddle.net/pangratz666/zc9AA/
Handlebars:
<script type="text/x-handlebars">
{{view App.SearchView placeholder="search" target="App.searchController" action="search"}}
{{#view Ember.Button target="App.searchController" action="search" }}
Search
{{/view}}
</script>
JavaScript:
App = Ember.Application.create({});
App.searchController = Ember.Object.create({
searchText: '',
search: function(){
console.log('search for %#'.fmt( this.get('searchText') ));
}
});
App.SearchView = Ember.TextField.extend(Ember.TargetActionSupport, {
valueBinding: 'App.searchController.searchText',
insertNewline: function() {
this.triggerAction();
}
});