Ember data: Insert new item at beginning of array instead of at the end - ember.js

In my application I'm displaying a timeline of messages. We retrieve them from the server in descending chronological order, newest to oldest.
3 - Howdy
2 - Greetings
1 - Mahalo
Our users also have the ability to add a new message which by default gets inserted at the end of the queue like so
3 - Howdy
2 - Greetings
1 - Mahalo
4 - I'm the new message, last as usual
When I submit, I'd like new messages to show up at the top. I've written a function before that reverses the array of items, but that wouldn't work for items already in the array.
4 - I'm the new message, first finally
3 - Howdy
2 - Greetings
1 - Mahalo
What would be the best approach in this case? The ideal would be for Ember Data to prepend to the content array rather than append. Is there another option which might be better?

For most scenarios involving sorting it's recommented to use Ember.SortableMixin, which is baked into Ember.ArrayController.
Please refer to this conceptual example in JSFiddle: http://jsfiddle.net/schawaska/tbbAe/
In this sample the model has a DateTime field named when, which I'm using for filtering:
App.Greeting = DS.Model.extend({
text: DS.attr('string'),
when: DS.attr('date')
});
App.Greeting.FIXTURES = [
{id: 1, text: 'First', when: '3/4/2013 2:44:52 PM'},
{id: 2, text: 'Second', when: '3/4/2013 2:44:52 PM'},
{id: 3, text: 'Third', when: '3/4/2013 2:44:52 PM'},
{id: 4, text: 'Fourth', when: '3/4/2013 3:44:52 PM'}
];
In the controller the only thing I have to do is to set the name of the property and the sorting direction:
App.SortingMixinController = Em.ArrayController.extend({
sortProperties: ['when'],
sortAscending: false
});
Then in my Handlebars template, I can use the {{each}} helper as I would do normally.
Because in this sample, all the dates are the same except for the Forth (which because of sorting appears first), and also because of SortableMixin, these values will be sorted through another property - I'm assuming the Id here.
The other approach I've taken in that fiddle is using a computed property. I'm not really sure about that approach as it seems to consume more resources and the code in App.SortingPropertyController is worthy of laugh, but sort of works to show possibilities.

Can you use basic JavaScript to drop in the new item at a given location in the array? Not sure if this is basic ajax + js objects or full blown ember-data models but this works for the simple js array example
var arr = [];
arr[0] = "Jani";
arr[1] = "Hege";
arr[2] = "Stale";
arr[3] = "Kai Jim";
arr[4] = "Borge";
console.log(arr.join());
arr.splice(2, 0, "Lene");
console.log(arr.join());
The output of the code above will be:
Jani,Hege,Stale,Kai Jim,Borge
Jani,Hege,Lene,Stale,Kai Jim, Borge

Related

EmberJS - How to remove an object from hasMany relationship and then repush it?

I try to remove a model from a relationship and later import it again by using pushPayload.
The relationship is only updated on one side but not an the reverse side.
this.get('store').pushPayload({
folder: {
id: 1,
name: 'My folder'
}
});
this.get('store').pushPayload({
item: {
id: 2,
name: 'My item',
parentFolder: 1
}
});
var folder = this.get('store').peekRecord('folder', 1);
var item = this.get('store').peekRecord('item', 2);
console.log('Parent folder id: ', item.get('parentFolder.id'), 'Items length', folder.get('items.length'));
item.get('parentFolder').get('items').removeObject(item);
console.log('Parent folder id: ', item.get('parentFolder.id'), 'Items length', folder.get('items.length'));
this.get('store').pushPayload({
item: {
id: 2,
name: 'My item',
parentFolder: 1
}
});
console.log('Parent folder id: ', item.get('parentFolder.id'), 'Items length', folder.get('items.length'));
I would expect to get the following output:
Parent folder id: 1 Items length 1
Parent folder id: undefined Items length 0
Parent folder id: 1 Items length 1
But I get this one:
Parent folder id: 1 Items length 1
Parent folder id: undefined Items length 0
Parent folder id: 1 Items length 0
The issue is the last Items length.
https://ember-twiddle.com/d58d95d5be0cc8750282b9cc48db6489
I reworked your twiddle with annotated comments on what happens with each push. The difference between push and pushPayload is that the latter calls the former once it has normalized the data into the format you see here.
https://ember-twiddle.com/c65c33ce0b1810258b75b5679b75d0e4?fileTreeShown=false&openFiles=controllers.application.js%2C
The short answer is that folder has local state (the removal of the item) that must be cleared in order for your server changes to take effect. While unloadRecord is one way to achieve this, that is an accidental side-effect and there are many better ways (one is shown in my twiddle using push, you could also do a pushObject to put the item back that you removed).
You need to unload the record if you are going to use pushPayload to add it back in and you wish the payload to "win" over what you have locally on relationships:
item.get('parentFolder').get('items').removeObject(item);
this.get('store').unloadRecord(item);
this.get('store').pushPayload({
item: {
id: 2,
name: 'My item',
parentFolder: 1
}
});

How to create a multi-use partial "template" in AngularJS?

I have a large list of items. Each item has it's own details.
In my main view/partial, I simply display a large list list of the item names.
When the user clicks on an item, I want the page to go to a partial which works as a "template", displaying information based on which list item is clicked, and hence possibly what the URL looks like. E.g. /listItem1/
This diagram below hopefully sums up what I want to achieve pretty clearly.
How can I do this?
Right now, I have a pretty standard set up in which I have all the information for each list item in an array of object literals, which is contained in a controller injected into the main app module. Like so:
var app = angular.module('app', [/*nodependencies*/]);
var controllers = {};
app.controller(controllers);
controllers.listController = function ($scope){
$scope.list = [
{name: 'List Item 1 Name', detail1: 'blahblah1', detail2: 'blahblah2'},
{name: 'List Item 2 Name', detail1: 'blahblah1', detail2: 'blahblah2'},
{name: 'List Item 3 Name', detail1: 'blahblah1', detail2: 'blahblah2'}
..... and so on
I know how to create basic views/partials as well. But what would be my next steps?
You can do what you want, using the built-in router which ships with AngularJS.
var app = angular.module('app', [/*nodependencies*/])
.config(function($routeProvider) {
$routeProvider
.when('/:itemId', {
templateUrl: '/path/to/partial',
controller : function($scope, $routeParams) {
$scope.item = $routeParams.itemId;
}
})
});
Basically, what the above means, is that if you browse to pdf/item/1
Then you will have access in your controller to $routeParams.itemId which will be equal to 1. You can then do whatever logic is necessary with this information on your partial to show the information you want.
Hope this helps.
Update
Please look at the controller, this is how you would get the param you passed via the URL, you would then do whatever it is you need to do with that param in the controller, and pass the data back to the view.
You can create a small directive that will use the multi-use partial to display each item on the list
Take a look at this working example (http://plnkr.co/edit/0jNVxRg6g3p8uxpustzz?p=preview)
var myApp = angular.module('myApp', []);
myApp.controller('listController', ['$scope', function ($scope) {
$scope.list = [
{
name: 'List Item 1 Name',
url: 'pdfs/item1.pdf',
detail: 'blahblah'
},
{
name: 'List Item 2 Name',
url: 'pdfs/item2.pdf',
detail: 'blahblah'
},
{
name: 'List Item 3 Name',
url: 'pdfs/item3.pdf',
detail: 'blahblah'
}
];
$scope.selectItem = function(item){
$scope.selected = item;
}
}]);
myApp.directive('listItem', [function () {
return {
restrict: 'A',
scope: {
item: '='
},
templateUrl: 'multiple-partial.html',
link: function (scope, element, iAttrs) {
}
};
}])

Jtable options array for drop down list or select list

I've been trying to create an object to use in a jtable as the options (for a select list).
I don't seem to have the format correct. The jtable.org website says it will take an array:
From the jtable.org website:
http://jtable.org/ApiReference#fopt-options
PhoneType: {
title: 'Phone type',
options: [{ Value: '1', DisplayText: 'Home phone' }, { Value: '2', DisplayText: 'Office phone' }, { Value: '2', DisplayText: 'Cell phone' }]
}
However, when I create an object like that:
var optionsObject = [];
optionsObject.push({Value: i, DisplayText: 'Hello' + i});
and then use it as a variable for the options in my jtable:
key: true,
options: optionsObject,
I don't get the items in the select list drop down. I do get something in the select list, but that looks like '[object Object]' instead of the actual items.
I'm not really sure what I'm doing wrong.
If I send an object that looks like this:
object.push('hello' + i);
I will get an item in the select list that looks like this 'hello0', as expected, but then the display text is also used as the value. I need to have an object with separate display texts and values.
Has anybody had any success with doing this?
After much trial and error, and debugger stops in the jtable scripting files, I learned that in order to use an object for the 'options' property of a field in jtable, the object must be in the following format
var optionsObject = new Object();
optionsObject[Value] = DisplayText;

Updating multiple objects with ember-data and non-rest action

Lets say I have a Customer who hasMany products
The api has a PUT: /customers/:id/cancel that cancels the customer and also cancels all the products and then returns the customer and the products for sideloading:
{
customer: {
id: 1,
name: "Customer Name",
canceled: true,
products: [1, 2] },
products: [
{id: 1, customer_id: 1, name: "Product 1", canceled: true},
{id: 2, customer_id: 1, name: "Product 2", canceled: true}
]
}
how would I:
Call that action on the customer?
Update all objects in the Ember Store?
For part 1. of your question,
You'll want to find it in the store var customer = store.find('customer', 1);
Then tell it to delete customer.destroyRecord();
And finally commit the delete customer.save();
For part 2. deleting the products, you'll have to do delete them manually in a forEach loop, tucked inside an Ember.run.once., as there is no cascading delete in ember data at this stage of the game.
Here is an example of this for a top level delete, so you would need to follow the same logic but for your products and follow it up with a .then that deletes your connected customer.
Single tier Example: http://jsbin.com/samegiqe/1/edit?js,output
Connected StackOverflow for further reading: How to delete all records associated with an ember model without clearing local Storage?
Complex mixin to solve this: http://jsbin.com/hupabovo/1/edit
Its connected StackOverflow: Delete associated model with ember-data
Hope that gets at what you were asking. Cheers!

Ext.form.ComboBox: Use template for displayField

Is there any way to apply a template to the selected value of a ComboBox? I'm using a template to display the drop down values of the ComboBox, but as soon as i select one, the plain value from the datastore is shown.
{
id: 'requestStatusCombo',
hiddenName: 'requestStatus',
tpl: '<tpl for="."><div class="x-combo-list-item">{statusCode:requestStatus}</div></tpl>',
fieldLabel: 'Status',
xtype: 'combo',
mode: 'local',
triggerAction: 'all',
store: new Ext.data.ArrayStore({
fields: ['statusCode'],
data: [['unassigned'],['assigned'],['closed']]
}),
valueField: 'statusCode',
displayField: 'statusCode'
}
I want to use my format function requestStatus to translate the statusCodes into locale spesific status names, and this works well for the drop down list, but as soon as I select something, the statusCode is shown.
So is it possible to assign a template to the displayField, or perhaps do some simple batch modification on the datastore? By processing the input through a reader maybe? Is there another <tpl for="?"> keyword that will make this happen?
I'm looking for some simple method utilizing the Ext library. If the only solution is to pre process the data, I'm capable of doing that myself.
I found a solution!
I changed my datastore, and added a reader to pre-process the status using a convert function:
{
id: 'requestStatusCombo',
hiddenName: 'requestStatus',
fieldLabel: 'Status',
xtype: 'combo',
mode: 'local',
triggerAction: 'all',
store: new Ext.data.Store({
data: [['unassigned'],['assigned'],['closed']],
reader: new Ext.data.ArrayReader({},[
{name: 'statusCode', mapping: 0},
{name: 'displayname', mapping: 0, convert: function(statusCode) {
return Ext.util.Format.requestStatus(statusCode);
}}
])
}),
valueField: 'statusCode',
displayField: 'displayname'
}
Examinig generated DOM you will notice that while list elements are DIVs, the field itself is html INPUT element. You can't have HTML within INPUT element... so no... no xtemplate here.
This does not mean it can't be done by extending Ext.form.ComboBox (or Ext.Component maybe)