Ember.js route problems with filter - javascript

My aim is to pass filtered data to my controller and then to my template. I've tried not using the filter and everything works as expected. If I even try to use a filter that lets everything through, I don't get any data. I've even tried using false instead of true and fiddling with the argument list of the filter.
I'm using ember-data fixtures to test this. I'm following the name conventions so much of the work is done for me under the hood. That all seems to be working though (otherwise the first example should also have a problem).
Works (arrives in the controller and eventually gets rendered on the page):
App.DomainDirRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('domain_dir');
}
});
Fails (controller gets an empty array):
App.DomainDirRoute = Ember.Route.extend({
model: function(params) {
return this.store.filter('domain_dir', function(item){
return true;
});
}
});
UPDATE (ATTEMPT 1):
Okay, so I've tried a couple of things based on Sam Selikoff's answer. I've defined 4 properties (2 filters, one map, one plain copy) in the controller and tried to display each in the mockup page. Only the property copyDomain gives a result.
Properties in controller:
filteredDomains: Ember.computed.filterBy('domain', 'domain', true),
upperCaseDomains: Ember.computed.map('domain', function(domain, index) {
return domain.toUpperCase() + '!';
}),
filteredDomains2: function() {
return this.get("model").filterBy('domain', true);
}.property('model.#each.domain'),
copyDomains: function(){
result = [];
this.forEach(function(item) {
result.pushObject(item);
})
console.log(result);
return result;
}.property('model.#each.domain')
Mockup:
<ul>
<li>filteredDomains</li>
{{#each domainDir in controller.filteredDomains}}
<li>domainDir.domain</li>
{{/each}}
</ul>
<ul>
<li>filteredDomains2</li>
{{#each domainDir in controller.filteredDomains2}}
<li>domainDir.domain</li>
{{/each}}
</ul>
<ul>
<li>upperCaseDomains</li>
{{#each domainDir in controller.upperCaseDomains}}
<li>domainDir.domain</li>
{{/each}}
</ul>
<ul>
<li>copyDomains</li>
{{#each domainDir in controller.copyDomains}}
<li>domainDir.domain</li>
{{/each}}
</ul>

Filtering is generally done at the controller/component level. store.find makes an AJAX request. Is your goal to only retrieve the filtered subset of data from the server, or to filter the data you already have at the view layer?
Typically if you're just wanting to do some live filtering, you'll do it in the controller. Leave your model hook as this.store.find('domain_dir') and add a filter in your controller:
App.DomainDirController = Ember.Controller.extend({
filteredDomains: function() {
return this.get("model").filterBy('someProp', true);
}.property('model.#each.someProp')
});
You should also check out the computed macros for some shorthands:
App.DomainDirController = Ember.Controller.extend({
filteredDomains: Ember.computed.filterBy('model', 'someProp');
});
Now in your template, you can do
{{#each domain in filteredDomains}}
...

Related

Ember and Handlebars Iterate Over a Set Array

I'm working on learning Ember and am trying to do some small ideas with it. Currently, I am trying to receive text field input to filter a list and return the matching results. I have all of this working, you know, the 'hard' stuff. However, the part that isn't working is Handlebars reading the 'title' property of my array that I am returning. It's just blank.
Here is my template:
<script data-template-name="application" type="text/x-handlebars">
{{input type="text" value=searchString placeholder="Search..."}}
{{filterMovies}}
<ul>
{{#each searchResults}}
<li>{{title}}</li>
{{/each}}
</ul>
</script>
And now my controller:
App.ApplicationController = Ember.Controller.extend({
filterMovies: function() {
var self = this,
searchString = self.get('searchString'),
searchResults = [],
filterArrLength = null,
theFullMovieList,
theFilteredMovieList = [];
if (!searchString) {
return;
}
var url = 'http://www.json-generator.com/api/json/get/clVKyWQSnC';
Ember.$.getJSON(url).then(function(data) {
theFullMovieList = data;
theFullMovieList.filter(function(movie) {
if (movie.title.toLowerCase().startsWith(searchString)) {
theFilteredMovieList.push(movie);
}
});
console.log(theFilteredMovieList);
self.set('searchResults', theFilteredMovieList);
});
}.property('searchString')
});
I have tried printing using {{this}}, {{this.title}}, {{searchResults.title}}, and {{title}} with no luck. However, logging the array shows the correct values.
Any ideas? View On CodePen
Your each syntax is invalid. You have to use new syntax:
<ul>
{{#each searchResults as |movie|}}
<li>{{movie.title}}</li>
{{/each}}
</ul>
See working demo on CodePen.

How to make hash-tags with meteorjs and iron:router?

Im trying to make hash-tags support with iron:router.
Now I have data of posts, each post has an array of tags, which renders as a list of links.
<ul class="list-unstyled list-inline">
{{#each tags}}
<li>
<a class="tag" href="{{pathFor 'postsTag'}}">{{this}}</a>
</li>
{{/each}}
</ul>
But I cannot understand how to get the value of a tag on click and to render posts according to this specific hash-tag.
I can get the value of a tag by Session on click
Template.post.events({
"click .tag": function(e) {
Session.set('tag',this.toString());
return false;
}
});
But i cannot make dynamic router on it.I tried something like that
Router.route('/posts/:tag',{
name: 'postsTag',
data: function() {
return Posts.find({tags:{$in: value_of_tag }});
}
});
If tags is a simple array of strings, you can query this field to match a particular value using this syntax :
Posts.find({
tags: "value"
});
$in is used when you want to search for documents having a particular field value among a set of values defined in an array.
Posts.find({
tags: {
$in: ["value1","value2","value3"]
}
});
This query would return posts having a tags array containing at least one of the specified value, which would be useful if you want to filter posts using multiple tag values.
http://docs.mongodb.org/manual/reference/operator/query/in/#op._S_in
If you want to define a route displaying only posts having a particular tag value, use this controller :
Router.route("/posts/:tag", {
name: "postList",
waitOn:function(){
return Meteor.subscribe("postsByTag",this.params.tag);
},
data: function() {
return Posts.find({
tags:this.params.tag
});
}
});

Looping over an ArrayController fails as it is not an Array even though the Controller has the correct model with data

I am attempting to use an ArrayController to handle displaying some data that will be swapped out on user clicks. I currently get this error, Uncaught Error: Assertion Failed: The value that #each loops over must be an Array. You passed App.CurrentListController but If I look at Ember Inspector I can see the CurrentListController and it has the model and the data in it. Basically the Stat page lets you see a bunch of stats and clicking on a specific stat pops up a modal and shows all the record that relate to that stat. If I just store the records on the StatController it works fine but then I cant sort/filter using the ArrayController. So it all works except for when I try and display the contents of CurrentListController it freaks out.
Thanks for any help or direction.
CurrentListController:
App.CurrentListController = Ember.ArrayController.extend({
sortProperties: ['name'], //Initial sort column.
sortAscending: true,
});
StatController:
App.StatController = Ember.ObjectController.extend({
needs:['currentList'],
currentList:[],
actions: {
viewBusiness: function(ids) {
console.log(ids)
var self = this
console.log(this.get('controllers.currentList').get('sortProperties'))
this.store.findByIds('business', ids.split(",")).then(
function(results)
{
$('#editModal').modal('show');
//self.set('currentList', results.sortBy(["name"]))
self.get('controllers.currentList').set('model', results)
console.log(self.get('controllers.currentList').get('arrangedContent'))
});
},
sortBy: function(prop){
var clController = this.get('controllers.currentList')
clController.set('sortProperties', prop)
clController.set('sortAscending', !clController.get('sortAscending'));
}
}
});
Stat Template:
{{#each business in App.CurrentListController}}
<tr {{bind-attr id=business.id}}>
<td>{{business.name}}</td>
<td>{{business.city}}</td>
<td>{{business.state}}</td>
<td>{{business.zip}}</td>
<td class="text-center">{{business.numVendors}}{{/link-to}}</td>
<td class="text-center">{{business.numClients}}{{/link-to}}</td>
</tr>
{{/each}}
App.CurrentListController is not an array. It's an object, a controller object. (btw it is not recommended to access the global namepsace [ ie. using anything with an uppercase letter ] in your template)
What you should do instead is:
App.StatController = Ember.ObjectController.extend({
needs:['currentList'],
currentList: Ember.computed.alias('controllers.currentList.model'),
...
This way you can access the underlying model of your currentList controller (which is an array) and make it available to your template as currentList.
{{#each business in currentList}}
...
{{/each}}

Computed.alias not updating bind-attr

I recently started using Ember.js. In my small application I currently have problems regarding Ember.computed.alias, because an {{#if}}-section is updated properly, but the bind-attr helper in the same template is not updated accordingly.
The application controller and the action influencing the value look as follows:
App.ApplicationController = Ember.ObjectController.extend({
isEditing: false,
actions: {
toggleEdit: function() {
var a = this.get('isEditing');
this.set('isEditing', !a);
}
}
});
The controller taking care of the template causing problems:
App.CategoriesController = Ember.ArrayController.extend({
needs: ['application'],
isEditing: Ember.computed.alias('controllers.application.isEditing'),
general: function() { // example depending on the alias
var result = this.filterBy('type', 1);
if (!this.get('isEditing')) {
result = result.filterBy('isHidden', false);
}
return result;
}.property('#each.type', '#each.isHidden', 'isEditing'),
// ......
The related template:
<ul id="categories">
{{#if isEditing}}YES!{{else}}NO!{{/if}}
{{#each general}}
<li {{bind-attr class=":general isEditing:editing"}}>
{{name}}
</li>
{{/each}}
</ul>
When the action toggleEdit is triggered, the {{#if}} section is updated and swaps between YES! and NO!, but the editing class is not applied to the list element. I tried encapsulated the alias into another property of the controller depending on the alias, but without success.
I assume it's a beginners mistake, but I can't figure out what I am overlooking.
Thanking you in anticipation.
isEditing is no longer in scope, use controller.isEditing, sorry phone response
Here's an example that would keep it in scope, but I'm fully qualifying it just to show you.
{{#each item in general}}
<li {{bind-attr class=":general controller.isEditing:editing"}}>
{{item.name}}
</li>
{{/each}}

Knockout foreach binding not working

I'm following John Papa's jumpstart course about SPA's and trying to display a list of customers loaded via ASP.NET Web API the knockout foreach binding is not working. The Web API is working fine, I've tested it on it's own and it is returning the correct JSON, because of that I won't post the code for it. The get method simply returns one array of objects, each with properties Name and Email. Although not a good practice, knockout is exposed globaly as ko by loading it before durandal.
I've coded the customers.js view model as follows
define(['services/dataservice'], function(ds) {
var initialized = false;
var customers = ko.observableArray();
var refresh = function() {
return dataservice.getCustomers(customers);
};
var activate = function() {
if (initialized) return;
initialized = true;
return refresh();
};
var customersVM = {
customers: customers,
activate: activate,
refresh: refresh
};
return customersVM;
});
The dataservice module I've coded as follows (I've not wrote bellow the function queryFailed because I know it's not being used)
define(['../model'], function (model) {
var getCustomers = function (customersObservable) {
customersObservable([]);
var options = {url: '/api/customers', type: 'GET', dataType: 'json'};
return $.ajax(options).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
var customers = [];
data.forEach(function (item) {
var c = new model.Customer(item);
customers.push(c);
});
customersObservable(customers);
}
};
return {
getCustomers: getCustomers
};
});
Finaly the model module was built as follows:
define(function () {
var Customer = function (dto) {
return mapToObservable(dto);
};
var model = {
Customer: Customer
};
return model;
function mapToObservable(dto) {
var mapped = {};
for (prop in dto)
{
if (dto.hasOwnProperty(prop))
{
mapped[prop] = ko.observable(dto[prop]);
}
}
return mapped;
}
});
The view is then simply a list, it is simply:
<ul data-bind="foreach: customers">
<li data-bind="text: Name"></li>
</ul>
But this doesn't work. Any other binding works, and I've looked on the console window, and it seems the observable array is being filled correctly. The only problem is that this piece of code doesn't show anything on screen. I've reviewed many times the files but I can't seem to find the problem. What's wrong with this?
You can use the knockout.js context debugger chrome extension to help you debug your issue
https://chrome.google.com/webstore/detail/knockoutjs-context-debugg/oddcpmchholgcjgjdnfjmildmlielhof
Well, I just spent a lot of time on an local issue to realize that the ko HTML comment format, if used, should be like this:
<!-- ko foreach: arrecadacoes -->
and NOT like this:
<!-- ko: foreach: arrecadacoes -->
: is NOT used after ko...
I know this question is a little old but I thought I'd add my response in case someone else runs into the same issue I did.
I was using Knockout JS version 2.1.0 and it seems the only way I can get the data to display in a foreach loop was to use:
$data.property
so in the case of your example it would be
$data.Name
Hope this helps
I don't see anywhere in your code that you've called ko.applyBindings on your ViewModel.
KO has a known issue while using foreach in a non-container element like the one above <ul> so you have to use containerless control flow syntax.
e.g.
<ul>
<!-- ko foreach: customers-->
<li data-bind="text: Name"></li>
<!-- /ko -->
</ul>
Ref: http://knockoutjs.com/documentation/foreach-binding.html

Categories

Resources