Angular filter related data - javascript

I am still fairly new to angular so I was wondering how to best do something like this:
I have a json file that returns a variety of items of type 'course' and type 'tutorial'. Tutorials are related to courses with a field
data = [
{ title: foo1, type: course, id:1},
{ title: foo2, type: course, id:2},
{ title: foo3, type: course, id:3},
{ title: t1, type: tutorial, id:4, related_course:2},
{ title: t2, type: tutorial, id:5, related_course:2},
{ title: t3, type: tutorial, id:6, related_course:3},
...
In my controller I have functions bound to $scope to allow me to filter by type.
Now in my template
<div ng-repeat="item in data | filter:isCourse">
... display course data
<div ng-repeat="item in data | filter.isTutorial">
... display tutorial data
I would like to find a way to make sure the tutorials displayed match the id of the currently displayed course.
Any suggestions?
Thanks!
- V

You should have then be specific with filters, but do make sure that JSON should be in correct format, as I can see most of the values are not there wrap in "(double quotes)
HTML
<div ng-repeat="course in data | filter: {type: 'course'}">
... display course data
<div ng-repeat="tut in data | filter: { related_course: course.id }">
... display tutorial data
Working Fiddle(Thanks #AWolf)

Related

ais-refinement-list in VueInstantSearch(Algolia) doesn't renders list

I have stucked with an issue using refinement list widget of algolia.
First of all my resulting data structure is like that:
[
{
objectID: 999,
title: 'some title',
categories: [
{
id: 444,
name: 'some name',
},
{...},
]
}
]
I have that type of structure on my page:
<ais-instant-search
:search-client="searchClient"
:initial-ui-state="{
index_Name: { query: searchedValue },
}"
index-name="index_Name"
>
<ais-index index-name="index_Name">
<ais-configure
:filters="facetsFilters"
:facets="['categories.id']"
:hits-per-page.camel="items"
/>
<ais-refinement-list attribute="categories.id" />
<div> ...Some other widgets nested in divs as ais-search-box, ais-sort-by, etc </div>
</ais-index>
</ais-instant-search>
Within ais-configure I have passed to filters a facetsFilters variable which contains string with such content:
"categories.id:1 OR categories.id:5"
and it works ok, I'm getting results within selected categories,
problems starts, when i try to get refinement-list here:
<ais-refinement-list attribute="categories.id" />
I have an empty list despite that on dashboard this attribute is added as an attributesForFacetings and in ais-configure filters parameters with categories.id in it also works well.
Any suggestions is highly appreciated !
Problem was in Dashboard of Algolia.
When we clicked on 'searchable' instead of 'filter only' radiobutton for chosen attributeForFaceting - everything starts working good.

Kendo grid columns with dynamic data

Need help with Kendo Grid, where in I have dynamic columns on Kendo Grid.
dynamicCols- Object is a object which has list of title and value properties which could be dynamic where it could have any number of objects in the list with title, value pair.
Kendo grid works well if JSON has a flat structure which has all properties at same level and I haven't come across this kind of hierarchial/JSON structure until now.
This grid also needs to support server side sorting and filtering with C# Web API, with Kendo Datasource API for server side sorting and filtering.
Existing kendo column mapping
var cols = [
{ field: 'name', title: 'Name', encoded: false },
{ field: 'id', title: 'Id' },
{ field: 'age', title: 'Age }
]
json = [{
name:'XYZ', id:123, age:45,
dynamicCols: [{title:'Gender',value:'Male'},
{title:'Veteran',value:'Yes'}]
}, {
name:'Jim', id:555, age:24,
dynamicCols: [{title:'Gender',value:'Male'},
{title:'Veteran',value:'No'}]
}, {
name:'Nick', id:557, age:78,
dynamicCols: [{title:'Gender',value:'Female'},
{title:'Veteran',value:'No'}]
}]
**Expected Grid**
Name Id Age Gender Veteran
XYZ 123 45 Male Yes
Jim 555 24 Male No
For Json2
json2 = [
{name:'XYZ', id:123, age:45,
dynamicCols: [{title:'SSN',value:'xx-xx-7891'}]
},
{name:'Jim', id:555, age:24,
dynamicCols: [{title:'SSN',value:'xx-xx-7892'}]
},
{name:'Nick', id:557, age:78,
dynamicCols: [{title:'SSN',value:'xx-xx-7895'}]
}];
**Expected Grid**
Name Id Age Gender SSN
XYZ 123 45 Male xx-xx-7891
Jim 555 24 Male xx-xx-7892
You have two options:
When you are done fetching the data and before creating the new Grid, resolve the JSON object and create flat columns object that the Grid accepts
Your second option is to forget the idea of creating dynamic columns and instead have a template column that dynamically resolves what it needs to display. In such cases you create an external function that you can call from your template. This way you do not end up with complicated and crappy templates. How to invoke external function from a template is covered here.
The easiest solution for me has been to make all columns and then end it by hiding those columns I did not need.
Even hiding around 50 columns did not take any noticable time.
(I had the luxury of knowing all potential columns that could appear)

Meteor : Iterate through array of Objects inside an Object

I'm currently trying to iterate through an array of Objects inside an Object.
I have my Collection "Proposals", and this is the corresponding Schema :
Schemas.ProposalsSchema = new SimpleSchema({
'content': {
type: String,
max: 140
},
'parties': {
type: [Object],
autoform : {
type: "select-multiple"
}
},
'parties.$._id': {
type: Object,
optional: true
},
'parties.$._id._str': {
type: String
},
'parties.$.name': {
type: String
}
});
I would like to iterate through the array of parties inside one of my template. I tried this :
{{#each proposals}}
<p>{{content}}</p>
<p>{{#each parties}} {{this.name}} {{/each}}</p>
{{/each}}
The content is displayed, but not the name of the different parties. Here's my template helper :
Template.proposalsIndex.helpers({
proposals: () => Proposals.find().fetch()
});
Do you know what do i do wrong?
Thank you in advance.
The code you posted looks ok.
Things you need to check are:
Is the "parties" field populated in a database? (you can run Proposals.find().fetch() in a meteor shell to check it).
If not, find out why it is not populated.
Is the "parties" field published to client? (you can run Proposals.find().fetch() in a browser console and see the results).
If not, check your publication.

AngularJS Nested Object Array Pathway

I have a factory, which goes into a controller, and I am trying to get data from that display on an HTML page. I am having trouble specifying an Object's pathway however.
My Factory:
app.factory('APIMethodService', function() {
var Head = "api.example.com";
return {
apis:
[{
accounts: [
{
v1: [
{
uri: Head+"/v1/accounts/",
item1: "AccountNumber",
item2: "MoneyInAccount"
}],
v2: [
{
uri: Head+"/v2/accounts/",
item1: "AccountNumber",
item2: "MoneyInAccount"
}]
}
],
customers: [
{
v1: [
{
uri: Head+"/v1/customers/",
item1: "CustomerName",
item2: "CustomerID",
item3: "CustomerEmail"
}]
}
]
}]
};
});
My Controller:
app.controller('APIController', function($scope, APIMethodService) {
$scope.title = "API";
$scope.apiList = APIMethodService;
$scope.accountList = $scope.apiList.accounts.v1;
$scope.accountList2 = $scope.apiList[0][0];
});
My HTML
<div ng-controller="APIController">
<div id="api" class="row">
<div class="col-xs-12">
<div class="row" style="font-size:20px">
{{title}} Page!
<table class="table table-striped">
<tr ng-repeat="api in apiList | orderBy:'uri' | filter:search">
<td>{{api.uri}}</td>
<td>{{api.item1}}</td>
<td>{{api.item2}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
The errors I get are in regards to the Controller trying to parse out the individual objects I wish to grab, like accounts or customers, and then any version v#, they may have.
So it will say something such as
TypeError: Cannot read property 'v1' of undefined
I just need some help specifying the proper pathways into my factory service.
You have a few problems. First, you are referring to the object returned from the factory incorrectly. APIMethodService is the factory that you're injecting, so you need to first reference the object that that factory is returning like this:
APIMethodService.apis
This will give you your entire JSON object.
From there, the rest of your object is made up of arrays of objects, so referring to 'v1' won't do you any good. You need to specify an index instead. If you want v1, you'll need:
APIMethodService.apis[0].accounts[0].v1
This will give you the v1 array, which again is an array of objects.
Customers would be:
APIMethodService.apis[0].customers[0].v1
The first problem you have is that the factory returns an object with a single property called apis. So basically this $scope.apiList.accounts.v1 should be $scope.apiList.apis.accounts.v1. Bu that's not all as this won't either work since dotting(.) into apis is an array you'd have to use the index. In this case it would be $scope.apiList.apis[0] and then you could .accounts[0].v1 which is also an array containing a single object.
Now if you can I would suggest to you that you'd change how you represent this data structure.
This is how you could do it.
app.factory('APIMethodService', function() {
var Head = "api.example.com";
return {
accounts: {
v1: {
uri: Head+"/v1/accounts/",
items: ["AccountNumber","MoneyInAccount"]
},
v2: {
... // skipped for brevity
}
},
customer: {
... // code skipped for brevity
}
};
});
And then it's just a matter of dotting into your APIMethodService-object like APIMethodService.accounts.v1.items[0] if you want the AccountNumber method name.
Constructing your url could then be done like this.
var baseUrl = APIMethodService.accounts.v1.uri; // 'api.example.com'
var url = baseUrl + APIMethodService.accounts.v1.items[0]; // 'AccountNumber'
// url = "api.example.com/v1/accounts/AccountNumber"
Again, this is one way you could do it but this can be further enhanced upon. The examples I provided are simply for demo purposes and this is not in any way the only way to do it.
Expanding upon recieved comments/questions your service (and data representation) could now look like this.
app.factory('APIMethodService', function() {
var Head = "api.example.com";
return {
accounts: {
v1: {
uri: Head+"/v1/accounts/",
items: [
{
name:'AccountNumber',
description:'Show the account number'
},
{
name:'AccountOwner',
description:'Show information about the owner of the account'
},
{
name:'MoneyInAccount',
description:'Show money in the Account'
}
]
},
v2: {
... // skipped for brevity
}
},
customer: {
... // code skipped for brevity
}
};
});
// Get descriptions
var accountNumberDescription = APIMethodService.accounts.v1.items[0].description; // 'Show the account number'
var accountOwnerDescription = APIMethodService.accounts.v1.items[1].description; // 'Show information about the owner of the account'
var moneyInAccountDescription = APIMethodService.accounts.v1.items[2].description; // 'Show money in the Account'
By using objects with properties like this it's alot easier to understand what you are trying to do. With arrays with indexes you'd have to know or take a look at the source to see what's going on. Here, someone viewing your code they can instantly understand that it is the description you are getting.

Typeahead templating, if/else

thanks for the help on this question: Typeahead result formatting, this is a follow up.
My JSON looks like
[{ name="Long Island", type="2", id="1234"}, { name="New York", type="1", id="5678"}]
In the drop down list I need to be able to seperate type 1 from type 2, so
Type 1 heading
type 1 item
type 1 item*
Type 2 heading
type 2 item
type 2 item
If there are no results for type 1, then don't show the heading. Same for type 2.
Is this possible with typeahead and a templating engine? I'm using Hogan but I'm not fussy.
The "Typeahead" way of doing this is to separate your dataset into 2 datasets, one that will only return "type 1" items and another that will return only "type 2" items. In typeahead, each dataset can have its own header, which will behave exactly the way you want it.
$autocomplete.typeahead([{
name: 'location 1',
remote: {
url: 'http://pathtomysite.com/%QUERY?type=1',
dataType: 'jsonp',
valueKey: 'name'
filter: function (parsedResponse) { return parsedResponse.locations; }
},
template: [
'<p class="repo-name">{{name}}</p>',
'<p class="repo-description">{{id}}</p>'
].join(''),
header: '<b>Type 1</b>'
engine: Hogan
}, {
name: 'location 2',
remote: {
url: 'http://pathtomysite.com/%QUERY??type=2',
dataType: 'jsonp',
valueKey: 'name'
filter: function (parsedResponse) { return parsedResponse.locations; }
},
template: [
'<p class="repo-name">{{name}}</p>',
'<p class="repo-description">{{id}}</p>'
].join(''),
header: '<b>Type 2</b>'
engine: Hogan
}])
Given that you don't have control over the JSON part, you have 2 options:
Option 1
Use 2 datasets, with the same query. In one of them, the filter will only return "type 1" entries, in the other the filter will only return "type 2" answers.
That's twice the calls to the JSON, so twice the load on your server. The client side will not see a delay, though, as the queries are concurrent.
That's a hackish solution, but OTOH it's clean (client-side-wise), and requires very little code.
Option 2
Use only 1 dataset, and so some work in your filter. Basically, return an array with an entry for the "type 1" header, then all the type 1 entries, then an entry for the "type 2" header, then all the type 2 entries.
The array is an array of objects. Each object will also have a class member. In the entries for the "type 1" and "type 2" headers set the class to "header" or something along the lines, and:
1) Have your template include the class.
2) Have your CSS make the class unselectable, unclickable, and styled the way you want it.
I like option #1 better.

Categories

Resources