Emberjs: Use a checkbox to display data - javascript

I have a data table with checkbox selection. If an item in the table is checked I want to display that data in another part of the page. Seems like it should be simple but I can't seem to figure it out. I made a js
JS Bin with data table example
Here is the controllers/route
App.IndexController = Ember.Controller.extend({
productLinks: function(){
return this.get('content');
}.property('model', 'isSelected'),
selectedProduct: function(){
var selectedProd = this.get('productLinks').filterBy('isSelected', true);
return selectedProd[0];
}.property('isSelected'),
isSelected: null
});
I would like this to evolve to only allowing a single selection, but I'll address that once I can get the data to display.

You need to define the dependency (property) correctly. You have the computed property depending on the isSelected property on the controller itself. You need to make it dependent on the isSelected property on each member of productLinks, which you do with the #each syntax.
selectedProduct: function(){
var selectedProd = this.get('productLinks').filterBy('isSelected', true);
return selectedProd[0];
}.property('productLinks.#each.isSelected')

Related

keep filter after ag-grid update

I'm trying to keep a grid's filter after updating a row.
When I click on a row in the grid, I open a dialog where I update the informations for this row, and then I look for the clicked row in the grid's rowData after that I update the corresponding record in the rowData with the values from the dialog, as following :
row[0].x = dg.x;
row[0].y = dg.y;
dg.gridOptions.rowData[index] = row[0];
dg.gridOptions.api.setRowData(newRows);
But after this I loose the filter on the grid, I did some search and I tried all the following solutions :
Setting the gridOptions property deltaRowDataMode to true.
filterParams: {apply: true, newRowsAction: 'keep'}
But none of these has worked.
How can I solve this ?
You can set the filterParams newRowsAction to keep, like that
dg.defaultColDef = { filterParams: { newRowsAction: 'keep'}} ;
refer to https://www.ag-grid.com/javascript-grid-filtering/index.php
Use this method (gridOptions.api.refreshCells()) instead of setRowData. Or if you need to use setRowData, save the filter model before the call and apply the filter again afterwards using these methods:
const model = this.gridOptions.api.getFilterModel();
//some code
this.gridOptions.api.setFilterModel(model);
I had same issue and this what I did to resolve it (kinda hacky).
Subscribe to rowDataChanged event of the ag grid.
(rowDataChanged)="onRowDataChanged($event)"
Inside the onRowDataChanged put your filtration logic
// Get a reference to the name filter instance
const filterInstance = this.gridApi.getFilterInstance('fieldNameHere');
// Call some methods on Set Filter API that don't apply the filter
filterInstance.setModel({
type: 'contains', // type of filter
filter: 'yourData' // set filter on data
});
// Get grid to run filter operation again
this.gridApi.onFilterChanged();
onRowDataChanged will be triggered twice, first when the grid clears everything and second when the data reloaded. So, you should put some conditions to avoid any errors.
For example, I needed to set the filter from the data that was loaded after the refresh this is what I did:
const filtered = this.rowData.filter(x => x.Id === this.Id);
if (filtered.length > 0) {
// Get a reference to the name filter instance
const filterInstance = this.gridApi.getFilterInstance('fieldNameHere');
// Call some methods on Set Filter API that don't apply the filter
filterInstance.setModel({
type: 'contains', // type of filter
filter: filtered[0].name // set filter on data
});
// Get grid to run filter operation again
this.gridApi.onFilterChanged();
}

select2: Is it possible to pass custom input and access it in methods

I want to pass custom property while creating select2. Example (my custom property being myFilterEnabled):
$('#mySelId2').select2({
myFilterEnabled: false, //Pass my initial state
query: function(query) {
var res = {
results: CityFilter.cities
};
query.callback(res);
}
});
And use it in the query or render functions. Like:
$('#mySelId2').select2({
myFilterEnabled: false,
query: function(query) {
var fltEnabled = this.myFilterEnabled; //Read current state
var res = {
results: fltEnabled ? [] : CityFilter.cities
};
query.callback(res);
}
});
This is so that, there is an initial state for the variable. But, it can change externally, and I want to check that state during each re-render/query.
Edit: Seems I made a mistake before posting. Above code seems to work. I am planning to add a common prefix like 'my' or 'myProj' so that it doesn't conflict with any variables of select2 itself.
Edit2: As mentioned, passing initial state and reading current state are working. I still need a way to change that state from outside. If select2 doesn't have a method for that I could set a data attribute on the element.
This is the full cycle that I wanted:
Set custom state -> Read custom state during query/render -> Change custom state on user action -> Trigger re-render on state change
This is how I managed to do it as of now:
1) I can pass a custom parameter in options while setting up select2
$('#mySelId2').select2({
myFilterEnabled: false,
query: function(query){ ...
2) I am able to read the custom parameter within the callbacks as
this.myFilterEnabled
3) I can set the custom parameter from outside as
$('#s2id_<myId>').data('select2').opts.myFilterEnabled = true;
3) After setting the property as shown above, i want select2 to
re-apply the query function. I can trigger change on
input.select2-input. But, there is a check to prevent re-execution
of query while the text remains the same. So, I go a step further
and call the updateResults function with a 'true' argument. That
forces updateResult to proceed to run query again. Example:
$('#s2id_<myId>').data('select2').updateResults(true);

Ember sort an enumerable array or add an new object to a regular array

I have a todo list.
The main template has an input for entering new todos and an outlet.
The outlet is for the todos.index template which displays each todo. I have made them srtable using jquery sortable. I sort them using a model property.
todos route:
model: function(){
return this.store.filter("todo", {}, function(todo){
if(todo.get("user") !== null && parseInt(todo.get("user").get("id")) === user_id)
return todo;
});
todos index route:
model: function(){
return this.modelFor('todos').sortBy('idx');
}
controllers:
App.TodosController = Ember.ArrayController.extend();
App.TodosIndexController = Ember.ObjectController.extend();
But when I add this .sortBy() method my returning array of objects is no longer live and any new todos i create arent added to the template. (they do get created and are in ember data and in my db, but they just arent being added to the template) - When you do sortBy the live array of ember data gets copied into an immmutable regular array.
If I leave out the sortBy() my new objects populate my template just fine.
any ideas of how to sort an array but keep it enumerble or how to refresh the template?
sortProperties is not working - I dont know why. My list of todos is populated by the objectController.
EDIT -
My problem was my own fault. I thought I had this
App.TodosIndexController= Ember.ObjectController.extend();
But what I actually had was:
App.TodoController = Ember.ObjectController.extend();
This left me without an explicit controller to do the sorting for me, so for me the solution was to create a new arrayController:
App.TodosIndexController= Ember.ObjectController.extend();
and use that to do the sorting. Though it wasnt the solution to my problem, my issue was my own obliviousness, it is the right way to sort items so I am going to mark #Kingpins answer as correct.
Make your controller sorted, and iterate over the controller (not the model) in your template.
App.TodosIndexController = Ember.ArrayController.extend({
sortProperties: ['idx'],
sortAscending: true
});
return your model to just the modelFor
model: function(){
return this.modelFor('todos');
}
Or you could create a computed property on the controller and watch the model for updates, updating the computed property
App.TodosIndexController = Ember.ArrayController.extend({
sortedTodos: function(){
return this.get('model').sortBy('idx');
}.('model.[]')
});
and again, return your model to just the modelFor
model: function(){
return this.modelFor('todos');
}
The other way to manually update the template, is to just get the model for TodosIndexController and manually use pushObject to add the new todos to it. It's just an array of items.

Can you bind a simple javascript array to your ember.js template?

I'm using ember.js RC1 + ember-data rev 11 (but I also need some plain ajax for configuration like models). I want to loop over a simple objects list and display the records (note -here I create just a basic array)
The content I have bound has the following custom find method defined
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
return this.records;
},
find: function() {
var self = this;
$.getJSON('/api/foo/', function(response) {
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
});
return this.records;
}
});
My other model uses this directly
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property()
});
Now inside my template
{{#each foo in related.listings}}
{{foo.name}}<br />
{{/each}}
The list loads up with whatever I put in the array by default (say I add a simple object using createRecord like so)
add: function(record) {
this.records.addObject(App.Foo.createRecord(record));
},
and when the template is rendered I see anything listed here... but as I put in the comments above, if I decide to remove records or null out the list that is bound it doesn't seem to reflect this in any way.
Is it possible to bind a simple array as I have and yet remove items from it using something basic such as splice? or even a drastic self.records = []; ?
self.records.splice(i, 1);
Even when I query the client manually after the splice or empty work it returns 0
console.log(App.Foo.all().get('length'));
Initially I see records, but then I see they are gone (yet the html doesn't change)
I understood your question this way, that the following remark is the point your are struggling with:
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
You are wondering, why your template is showing no empty list? It's because you did not tell Ember when to update the template. You can tell Ember this way:
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property("App.Foo.records.#each")
});
Now Ember knows, whenever something is added or removed from your array, it should update the listings property of your model. And therefore it knows that your view needs rerendering.
One additional remark to the orignal question regarding "simple javascript arrays". When you use Ember, you actually do not instantiate simple js arrays. When you declare:
var a = []; // is the same as -> var a = Ember.A();
Ember does some magic and wraps in an enhanced ember version of an array (Ember.NativeArray), which enables you to use such property dependency declarations mentioned above. This enables Ember to use ArrayObservers on those arrays, although they may feel like a plain JS Array.
You need to use the set method when you modify properties and get when you return them, or else Ember won't be able to do its magic and update the template.
In your case, there is an additional problem, which is that in find(), you return a reference to records before your asynchronous getJSON call replaces it with a new empty array. The calling method will never see the new array of records. You probably want to use clear() instead.
Your model should look something like this:
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
// can't use 'this.get(...)' within a class method
return Ember.get(this, 'records');
},
findAll: function() {
var records = Ember.get(this, 'records');
$.getJSON('/api/foo/', function(response) {
records.clear();
// in this case my json has a 'foos' root
response.foos.forEach(function(json) {
this.add(json);
}, this);
}, this);
// this gets updated asynchronously
return records;
},
add: function(json) {
// in order to access the store within a
// class method, I cached it at App.store
var store = App.get('store');
store.load(App.Foo, json);
var records = Ember.get(this, 'records');
records.addObject(App.Foo.find(json.id));
}
});
Note that the addObject() method respects observers, so the template updates as expected. removeObject() is the corresponding binding-aware method to remove an element.
Here's a working jsfiddle.

How to initialize a Knockout viewmodel when initial viewmodel load is empty

I am using Knockout to implement a course list selection tool. I am using the approach below to populate the data (MVC3/Razor), so that when the viewmodel is initially populated, I have no issues working with each KO array (i.e. CourseList, ScheduleList). However, when the initial load from the server returns zero rows, meaning that the viewmodel 'ScheduleList' property is empty, then it's not possible to call any methods such as .push() or .removeAll(). Presumably this means that the observable array was never created since there was nothing to fill it with. When the model is filled, the ScheduleList property is populated with a List. What is the best way to instantiate it when the MVC action returns it as empty? There is a jsFiddle that seems to address it, but when I try to use the 'create' option, it renders my entire model blank. I am not sure what the syntax is of the 'create' option. The jsFiddle is here: http://jsfiddle.net/rniemeyer/WQGVC/
// Get the data from the server
var DataFromServer = #Html.Raw(Json.Encode(Model));
// Data property in viewmodel
var data = {
"CourseList": DataFromServer.CourseList ,
"ScheduleList": DataFromServer.ScheduleList
};
$(function() {
// Populate Data property
viewModel.Data = ko.mapping.fromJS(data);
// ko.applyBindings(viewModel, mappingOptions);
ko.applyBindings(viewModel);
});
When the initial page load does not populate ScheduleList, then the following code throws an error. If the initial page load contained data, then you could call .removeAll() and .push() etc.
var oneA= 'abc';
// push not working
this.Data.ScheduleList.push( oneA );
Set up your mapping parameters to make it so on creation, you give it a certain structure. Then it will do the updates for you.
What is most likely happening is that your DataFromServer doesn't actually contain a ScheduleList property at all. So when it is mapped, a corresponding property is never made. The mapper will only map existing properties to observables.
You need to set in your create options for the view model to add empty arrays when either array is not set. That way, your view model will end up with corresponding observable arrays in place.
By ensuring that CourseList or ScheduleList is an array, the mapped view model will map them as observableArray objects so your code will work as you expected.
var DataFromServer = {
'CourseList': [1,2,3]
//, 'ScheduleList': []
};
var dataMappingOptions = {
'create': function (options) {
var data = options.data;
data.CourseList = data.CourseList || [];
data.ScheduleList = data.ScheduleList || [];
return ko.mapping.fromJS(data);
}
};
viewModel.Data = ko.mapping.fromJS(DataFromServer, dataMappingOptions);
var data = {
CourseList: DataFromServer.CourseList || ko.observableArray([]) ,
ScheduleList: DataFromServer.ScheduleList || ko.observableArray([])
};

Categories