Ember - Clearing an ArrayProxy - ember.js

On the Ember MVC TodoApp there is an option "Clear all Completed".
I've been trying to do a simple "Clear All".
I've tried multiple things, none of them work as I expected (clearing the data, the local storage and refreshing the UI).
The ones that comes with the sample is this code below:
clearCompleted: function () {
this.filterProperty(
'completed', true
).forEach(this.removeObject, this);
},
My basic test, that I expected to work was this one:
clearAll: function () {
this.forEach(this.removeObject, this);
},
Though, it's leaving some items behind.
If I click the button that calls this function in the Entries controller a couple times the list ends up being empty. I have no clue what's going on! And don't want to do a 'workaround'.
The clearCompleted works perfectly by the way.

The answer depends on what you really want to know-- if you want to clear an ArrayProxy, as per the question title, you just call clear() on the ArrayProxy instance e.g.:
var stuff = ['apple', 'orange', 'banana'];
var ap = Ember.ArrayProxy.create({ content: Ember.A(stuff) });
ap.get('length'); // => 3
ap.clear();
ap.get('length'); // => 0
This way you're not touching the content property directly and any observers are notified (you'll notice on the TodoMVC example that the screen updates if you type Todos.router.entriesController.clear() in the console).
If you're specifically asking about the TodoMVC Ember example you're at the mercy of the quick and dirty "Store" implementation... if you did as above you'll see when you refresh the page the item's return since there is no binding or observing being done between the entry "controller" and the Store (kinda dumb since it's one of Ember's strengths but meh whatev)
Anywho... a "clearAll" method on the entriesController like you were looking for can be done like this:
clearAll: function() {
this.clear();
this.store.findAll().forEach(this.removeObject, this);
}

Well, this worked:
clearAll: function () {
for (var i = this.content.length - 1; i >= 0; i--) {
this.removeObject(this.content[i]);
}
},
If someone can confirm if it's the right way to do it that would be great!

Related

Reload model/update template on createRecord save

I see this question is being ask all over again still don't find solution that works for such a trivial task.
This url displays a list of navigations tabs for workspaces.
http://localhost:4200/users/1/workspaces
Each of tab resolves to
http://localhost:4200/users/1/workspaces/:wid
Also on the I have a button that suppose to create a new workspace as well as new tab.
Here how controller for looks:
export default Ember.Controller.extend({
actions: {
newWorkspace: function () {
this.get('currentModel').reload();
var self = this;
var onFail = function() {
// deal with the failure here
};
var onSuccess = function(workspace) {
self.transitionToRoute('dashboard.workspaces.workspace', workspace.id);
};
this.store.createRecord('workspace', {
title: 'Rails is Omakase'
}).save().then(onSuccess, onFail);
}
}
});
When I click on button I see in ember inspector new record indeed created as well as url redirected to id that represents newly created workspace.
My question is how to force model/template to reload. I have already killed 5h trying model.reload() etc. Everything seem not supported no longer. Please please help.
UPDATE
When adding onSuccess
model.pushObject(post);
throws Uncaught TypeError: internalModel.getRecord is not a function
I believe you should call this.store.find('workspace', workspace.id) for Ember Data 1.12.x or earlier. For 1.13 and 2.0 there are more complicated hooks that determine whether or not the browser should query the server again or use a cached value; in that case, call this.store.findRecord('workspace', workspace.id, { reload: true }).
I do not know if this help. I had a similar problem. My action was performed in the route. Refresh function took care of everything.

Ember: Equivalent of document.getElementById('foo').addEventListener

I need to add a webkitTransitionEnd event listener to one of the DOM elements in my EmberView.
This javascript (DOM) equivalent would be:
function transitionEnded() {
console.log("transition ended");
}
document.getElementById('foo').addEventListener(
"webkitTransitionEnd",
this.transitionEnded,
true);
I've tried the following:
var MessageView = Ember.View.extend({
...
transitionEnded: function() {
console.log("Transition Alert!");
},
actions: {
closeMessageWithTransition: function(){
var elem = document.getElementById('transitionThis');
elem.addEventListener(
"webkitTransitionEnd", this.transitionEnded, true);
// Trigger the transition here
}
...
I've also tried using:
this.$("#transitionThis").get(0).addEventListener(...);
instead of using:
var elem = document.getElementById('transitionThis');
elem.addEventListener(...);
but to no avail. The transition happens, but I do not get any events nor do I see errors in the console.
I can confirm that the document.getElementById method selects the right element. So, where are my event handlers going?
EDIT 1: Didn't realize there was an emberjs.jsbin.com. So, here you go:
Emberjs.jsbin
PS: I do realize that the element I'm attaching a listener to ends up getting destroyed later when in transitionTo('messages') but I've commented out that bit and still no effect.
EDIT 2: I've added an alternate method of trying to catch the event using an EventManager as outlined in the Ember.View docs.
Alternate Method
It looks like it's using animation, not transition, webkitAnimationEnd is the appropriate hook.
http://emberjs.jsbin.com/awEWUfOd/4/edit

Using Backbone.js in SaaS without templates

I am designing a SaaS application and have been directed to Backbone.js. The service in part tracks DOM events such as how many of each have occurred and then applies scores based on this information.
Decoupling data into Models and Collections is very appealing, but before I go any deeper I want to enquire as to whether it is the right tool for the job.
I want to work with existing DOM elements written in the HTML of a site owners page rather than create JavaScript templates. I will therefore be tracking DOM events on existing elements which then update the data model. The site owner making use of the service will then be able to use the data in the Model to create their own Views and render their own templates specific to their needs.
I understand that I will need to use Backbone.View to track the events, and from what I have read so far it seems Backbone has the flexibility to allow this. However, I haven’t seen any examples in my research of Backbone used to track a bunch of events on a number of form elements.
Take this code for example:
App.Models.Event = Backbone.Model.extend({
defaults: {
clicks: 0,
dblClicks: 0,
tabs: 0,
kbdFunctions: 0
},
urlRoot: 'events'
});
App.Views.Event = Backbone.View.extend({
model: new App.Models.Event(),
events: {
'click input' : 'clickCount',
'dblclick input' : 'dblClickCount',
'tabEvent input' : 'tabCount',
'kbdEvent input' : 'kbdEventCount'
},
initialize: function () {
this.el = $('[data-transaction=start]');
},
clickCount: function (e) {
console.log('click counted');
},
dblClickCount: function (e) {
console.log('double click counted');
},
tabCount: function (e) {
console.log('tab counted');
},
kbdEventCount: function (e) {
console.log('keyboard event counted');
}
});
I want to be able to track clicks, double clicks, tabs and other custom keyboard events that occur on input, textarea, select options and button that are contained within the [data-transaction=start] element. Firstly, is this an applicable use case for Backbone, and secondly, if so what is the best way of adding multiple elements within the Backbone.View events object literals? I haven't seen any examples of this in the documentation or anywhere else, but it would be good if I could add a variable into this like:
...
var someVariable = input, textarea, select, button;
events: {
'click someVariable' : 'clickCount',
...
Events are assigned by Backbone using the delegateEvents method in view. This method is called AFTER your view initialize method (code reference)
so you could pass your variables in view constructor
myView = new App.Views.Events ( someVariable )
in your initialize method, you can assign events:
initialize: function(someVariable) {
//assign this.events from someVariable as you would like
}
EDIT:
just read in Backbone documentation:
The events property may also be defined as a function that returns an
events hash, to make it easier to programmatically define your events,
as well as inherit them from parent views.

How to live update jqPlot graph with ember.js?

I know how to update and redraw a jqPlot object without using ember...
I created the following fiddle to show the "problem": http://jsfiddle.net/QNGWU/
Here, the function load() of App.graphStateController is called every second and updates the series data in the controller's content.
First problem: The updates of the series seem not to propagate to the view.
Second problem: Even if they would, where can i place a call to update the plot (i.e. plotObj.drawSeries())?
I already tried to register an observer in the view's didInsertElement function:
didInsertElement : function() {
var me = this;
me._super();
me.plotObj = $.jqplot('theegraph', this.series, this.options);
me.plotObj.draw();
me.addObserver('series', me.seriesChanged);
},
seriesChanged: function() {
var me = this;
if (me.plotObj != null) {
me.plotObj.drawSeries({});
}
}
But that didn't work...
Well, figured it out, see updated fiddle.
The secret sauce was to update the whole graphState object (not just it's properties) in App.graphStateController:
var newState = App.GraphState.create();
newState.set('series', series);
me.set('content', newState);
And then attach an observer to it in the App.graphStateView:
updateGraph : function() {
[...]
}.observes('graphState')
The updateGraph function then isn't pretty, since jqPlot's data series are stored as [x,y] pairs.
The whole problem, i guess, was that the properties series and options in the App.graphState object itself are not derived from Ember.object and therefore no events are fired for them. Another solution may be to change that to Ember.objects, too.

When is the template (.tpl) rendered for an Ext JS Component?

I am trying to inject another component into an element that is rendered by the template of another Coomponent..but in the afterrender event, the template is yet to be rendered so the call to Ext.get(el-id) returns null: TypeError el is null.
tpl:
new Ext.XTemplate(
'<tpl for=".">',
'<ul>',
'<li class="lang" id="cultureSelector-li"></li>',
'</ul>',
'</tpl>'
),
listeners: {
afterrender: {
fn: function (cmp) {
console.log(Ext.get('cultureSelector-li')); // < null :[
Ext.create('CultureSelector', {
renderTo: 'cultureSelector-li'
});
}
}
},
So when can I add this component so that the element is targeting has been created in the DOM?
I think it depends on the component that you are working with. For example, the Data Grid View has a "viewready" event that would suite your needs, and depending what you are attempting, the "boxready" function could work for combo box (only the first render though). Other than that, you can either go up through the element's parent classes searching for the XTemplate render function being called (might be in the layout manager) and extend it to fire an event there, or risk a race condition and just do it in a setTimeout() call with a reasonable delay.
I ended up having to do the work myself. So, I now have the template as a property called theTpl, and then rendered it in beforerender, and then i was able to get a handle on the element in afterrender. This seems wholly counter-intuitive, does anyone have any insight?
beforeRender: {
fn: function (me) {
me.update(me.theTpl.apply({}));
}
},
edit in fact I just extended Component thus:
Ext.define('Ext.ux.TemplatedComponent', {
extend: 'Ext.Component',
alias: 'widget.templatedComponent',
template: undefined,
beforeRender: function () {
var me = this;
var template = new Ext.XTemplate(me.template || '');
me.update(template.apply(me.data || {}));
me.callParent();
}
})
...template accepts an array of html fragments
Turns out I was using the wrong things - apparently we should be using the render* configs for this type of thing (so what are thetpl & data configs for?)
Here's a working fiddle provided for me from the sencha forums:
http://jsfiddle.net/qUudA/10/