How to get related object in angular JS template? - javascript

It should be noted that I'm very new to Angular.
I have the following JSON I get from Django Rest Framework:
api/movie/1
{
"id": 1,
"name": "Mr & Mrs Smith",
"actors": [
1,
2,
]
}
api/actor/1
{
"id": 1,
"name": "Angelina Jolie",
}
api/actor/2
{
"id": 2,
"name": "Brad Pitt",
}
I made a movie detail page following the Angular tutorial, using $resource.
This is the movie-detail.component.js
angular.
module('movieDetail').
component('movieDetail', {
templateUrl: 'static/partials/movie-detail/movie-detail.template.html',
controller: [ '$routeParams', 'Movie',
function MovieDetailController($routeParams, Movie) {
var self = this;
self.movie= Movie.get({movieId: $routeParams.movieId})
}
]
});
In the movie HTML template, I have access to the "actors" id via "$ctrl.movie.actors". But I can't figure out if there is a way to use this id to ask the server for the 'actor' object. Something like :
Actor.get({id}) to incorporate them in the movie details template.
TL;DR
What I can do now :
Actors:
<ul>
<ling-repeat="actor in $ctrl.movie.actors"> {{actor}} </li>
</ul>
result:
Actors
1
2
And I want something along the lines of :
Actors:
<ul>
<ling-repeat="actor in $ctrl.movie.actors"> {{actor.name}} </li>
</ul>
result:
Actors
Angelina Jolie
Brad Pitt
How can this be achieved ?
Thank you for any replies!

First of All you are using Angularjs 2 and Angularjs 2 is way different then Angularjs 1.*.
In this case its more javascript object manipulation rather Angularjs tricks
you can try below ways to achieve this
option1:
if you are able to edit your rest call in Django, change your code to return the movie and actor details in one js like this
{
"id": 1,
"name": "Mr & Mrs Smith",
"actors": [
1: {"id": 1,"name": "Angelina Jolie"},
2: {"id": 1,"name": "Angelina Jolie"},
]
}
And try to ng-repeat for you actor object under movie. or you can separate both the calls as your requirement.
option2:
You need to do a lazy loading at the time of loading. once your movie object is loaded, you need to call actor rest url to get all the actor object required and append them in the existing object if you are unable to make a single rest call.then in the template you can show actor detail as above.

Related

Displaying Nested Objects (Numeric) After Making Service Call

I'm currently having an issue accessing nested objects that are referred to through numbers. I made a service call to retrieve a JSON object, and then mapped each field to another object. I'll be using this object to display each field in the HTML.
My problem is occurring when I reach the nested objects. Here's an example :
{
"name": {
"title": "mr",
"first": "something",
"last": "something"
},
"role": "something",
"projects": {
"0": {
"title": "something",
"account": "something",
"steps": {
"total": 30,
"completed": 28
},
"dueDate": "2021-07-19 09:00:00",
"status": "fine"
},
"1": {
"title": "something",
"account": "something",
"steps": {
"total": 10,
"completed": 5
},
"dueDate": "2021-07-20 09:00:00",
"status": "fine"
},
}
}
The projects field gets tricky when trying to display all projects in the HTML. At the moment, I've created a person variable initialized to an empty array, and I add all of the fields from t he subscription to it. To solve this problem, I figured I should create a separate variable such as projects: any = []; and then set it to a new field in the person variable. Then iterate through it using an *ngFor, and display every project. Something like this
<li *ngFor="let project of person.projects">
{{ project }}
</li>
However that approach still doesn't reach the nested fields. How do I access the numbered objects here, and generally iterate through all of the nested fields?
Any advice will be very helpful. Thank you in advance.
I think you are looking for the KeyValuePipe
You would use it like this:
<li *ngFor="let item of person.projects | keyvalue">
{{ item.value }}
</li>
But based on your comment on doing this for more nested values, it might be worth flattening the data a bit in the component to simplify the template logic.

Dust js How to check whether the condition is true at least once in a loop

Here is my data that has to be rendered:
var data = {
"foo" : "1",
"project": [
{
"name": "Project 1",
"status": "red"
},
{
"name": "Project 2",
"status": "green"
},
{
"name": "Project 3",
"status": "red"
}
]
};
I want to loop through this data and print projects with status red, yellow, and green separately. If there is no project of particular status type then I want to display no yellow/red/green project. I am able to display the projects under their particular status but how do I find whether there is no project of the current status type.
My dust template is:
<h1>red</h1>
{#project}{#eq key=status value="red"}{name}
{/eq}{/project}
<h2>green</h2>
{#project}{#eq key=status value="green"}{name}
{/eq}{/project}
<h2>Yellow</h2>
{#project}{#eq key=status value="yellow"}{name}
{/eq}{/project}
Please see: http://jsfiddle.net/xuLSq/
I'm not absolutely sure, but DustJS doesn't support this use case.
Since it's a templating engine, I guess they encourage you to expose plain data in your model and avoid such kind of logic in the template itself.
You might split your model as follows:
redProjects
greenProjects
yellowProjects
And then you won't require DustJS syntax to get this working!

Populate AngularJS {{expression}} within ng-repeat using a second array

I'm completely rebuilding my website (originally hacked together with Wordpress) using Laravel and AngularJS. It's been a massive learning experience and I think I'm nearly there but for one problem.
On my site 'schemes' (or courses) are made up of 'units' which are made up of 'lessons'. Retrieving this data is fine, using Eloquent I retrieve valid JSON like this made up example...
[
{
"id": "1", //Scheme Id
"title": "Sports",
"description": "This is a Sports course!",
"units": [
{
"id": "1",
"title": "Tennis",
"lessons": [
{
"id": "6",
"title": "Serving"
},
{
"id": "7",
"title": "Hitting the ball with top-spin"
}
]
},
{
"id": "2",
"title": "Athletics",
"lessons": [
{
"id": "1",
"title": "Long Jump"
},
{
"id": "2",
"title": "Hurdling Technique"
}
]
},
{
"id": "4",
"title": "Golf",
"lessons": [
{
"id": "4",
"title": "Pitching"
},
{
"id": "5",
"title": "Putting"
}
]
}
]
}
....
]
Separately I have a simple array of completed lesson ids for a particular user like this...
[2, 6, 8, 9] ///User has completed lessons with ids of 2,6,8 and 9
In my view I'm using nested ng-repeat loops like so...
...
<div ng-controller="SchemesController">
<div ng-repeat="scheme in schemes">
<h1>{{scheme.title}}</h1>
<div ng-repeat="unit in scheme.units">
<h3>{{unit.title}}</h3>
<div ng-repeat="lesson in unit.lessons">
<div>{{lesson.title}}: {{status}}</div>
</div>
</div>
</div>
</div><!--[end of ng-controller="SchemesController"]-->
....
SchemesController (v simple!) looks like this...
var app = angular.module('schemesApp', []);
app.controller('SchemesController', function($scope){
$scope.schemes=jsonData;
});
The problem is I have no idea how to populate the {{status}} field which I want to state simply 'Complete' or 'Incomplete. I investigated whether I could somehow add this info to my original array like this...
"lessons": [
{
"id": "6",
"title": "Serving",
"status": "Complete" //populated somehow
},
{
"id": "7",
"title": "Hitting the ball with top-spin",
}
]
but I got nowhere slowly. Is there a way to do this (I've played around with underscore.js and felt this could help?).
Or do I populate {{status}} from creating and calling a javascript function?!?
ANY help that anyone could offer would be incredible. I'm a school teacher and for some sadistic reason I find a bit of programming/web design a fun use of my spare time so I apologise if this is a stupid question. THANKS in advance!!!
btw if anyone has a better 'title' for this question then please let me know.
I'm assuming you don't need to persist the status back to the database...
This is where you're having the problem:
<div>{{lesson.title}}: {{status}}</div>
You really don't need to store the status in your data model, because it's just used for presentation purposes.
Let's say your array of completed lessons is defined like this:
$scope.completedLessons = [1, 2, 3, 4, 5] // Or however you'd assign it
You need to create a function in your scope like this:
$scope.isLessonCompleted = function(lessonId) {
return $scope.completedLessons.indexOf(lessonId) > -1;
};
Then you need to change the html from above to this:
<div>{{lesson.title}}: {{isLessonCompleted(lesson.id) && 'Complete' || 'Incomplete'}}</div>
If lessons are also a model and each lesson should have a status, which isn't a column/field in your table but is something you'll add logic to determine, you could add a custom model accessor by adding the following to your models/Lesson.php:
// Append custom accessor attributes
protected $appends = ['status'];
public function getStatusAttribute() {
// Add logic here
return 'Complete';
}
This way, when you use Eloquent to retrieve your data, you'll also see a status attribute as part of the object, so you could then access it as usual $lesson->status (PHP) or lesson.status (JS).
For more information, see the official Laravel documentation on accessors and mutators

Angular $http.get

I'm trying to show all lists and the tasks associated with each list. In my controller I have:
$http.get('api/list/').success(function (data) {
$scope.lists = data;
$scope.tasks = data[0].Task;
});
This works for the first item but of course data[0].Task needs to be dynamic. The problem I'm having is that this is being called once for each list. I tried using a variable but it gets reset to it's original value. I've also tried using a callback but no luck. I'm not sure what I'm overlooking or if I'm going about it all wrong.
Your best bet is to wrap the http.get in a factory and let it return new representations of your Lists that have the tasks in them. This way you get new references and it won't overwrite your existing objects. Essentially, you want the http.get to return new List objects in its success resolution.
After that, the controller gets the promise resolution, takes the new list object, and binds it into something thats on the scope. This will filter through to the rest of the page and let you preserve existing lists/tasks for the life of the page.
Your GET api/list/ request you probably return something like this:
[
{
"id": 1,
"name": "List #1",
"tasks": [
{
"id": 1,
"name": "Task #1 on List #1"
},
{
"id": 2,
"name": "Task #2 on List #1"
},
{
"id": 3,
"name": "Task #3 on List #1"
}
]
},
{
"id": 2,
"name": "List #2",
"tasks": [
{
"id": 1,
"name": "Task #1 on List #2"
},
{
"id": 2,
"name": "Task #2 on List #2"
},
{
"id": 3,
"name": "Task #3 on List #2"
}
]
}
]
This is assuming that you always want to return the associated tasks in an api/list/ command.
You then only need to call this once every time you want to refresh all lists and all tasks.
You should have a single controller which is bound to a view, in which your $http.get is called. It should set $scope.lists = data on success.
In your view you simply need two nested ng-repeat tags. For example, you could use unordered lists:
<div ng-controller="ListsController">
<ul>
<li ng-repeat="list in lists">
<ul>
<li ng-repeat="task in list.tasks">
</li>
</ul>
</li>
</ul>
</div>
I haven't used angular but I'm pretty sure this is all you need to do. A single AJAX call will populate a <li> element for each list, with nested <li> elements for each task belonging to that list.
If each list has a task and you want a list of them you can do this:
$scope.tasks = data.map(function(obj){return obj.task;})
map creates an array based on what the function returns for each list.

Backbone.js + Relational + AMD ... bootstrapping relationals

I feel sort of lost and overlooking something, but i am not sure how to approach to this and even not very much sure how to ask...
First of all, i am using AMD approach (with curl.js library), which makes this probably more difficult, but i am not giving up on AMD because of this problem.
I have this structure of bootstrap data from the server, stored in 'window.bootstrap' property.
Departments = [
{"Id": 1, "Name": "Early Collections" },
{"Id": 2, "Name": "Collections" }
]
Blocks = [
{"Id": 1, "Code": "K", "Department": 1 },
{"Id": 2, "Code": "A", "Department": 2 }
]
Now i am confused about approach to this. Here is my 'DataModel/Block' module:
define [
'Collection/DepartmentCollection'
'DataModel/Department'
], (DepartmentCollection, Department) ->
Backbone.RelationalModel.extend
relations: [
type: Backbone.HasOne
key: 'Department'
relatedModel: Department
collectionType: DepartmentCollection
]
Module 'DataModel/Department' is just plain RelationalModel without any relations. Also every mentioned Collection here is also plain without anything but reference to Model like this:
define ['DataModel/Department'] , (Department) ->
Backbone.Collection.extend
model: Department
And finally, here goes Bootstrap module, which looks like this:
define [
'DataModel/Department'
'Collection/DepartmentCollection'
'DataModel/Block'
'Collection/BlockCollection'
] , (Department, DepartmentCollection, Block, BlockCollection) ->
model = Backbone.RelationalModel.extend
relations: [
type: Backbone.HasMany
key: 'Departments'
relatedModel: Department
collectionType: DepartmentCollection
,
type: Backbone.HasMany
key: 'Blocks'
relatedModel: Block
collectionType: BlockCollection
]
data = window.bootstrap || {}
boot = new model
boot.get('Departments').reset data.Departments || []
boot.get('Blocks').reset data.Blocks || []
return boot
I would expect from this, that it would find Departments for those Blocks and assign models there, but calling
console.debug ins.get('Blocks').at(0).get('Department')
...gets me undefined.
But this is not the end. I will be having other entities from server with relation to Department too. And i would like to see, it automatically attaches Department from that bootstrap, so i can use it transparently.
I don't know if i had just misunderstood this relational library, or it's not AMD ready. Any help is appreciated.
Potential scoping / name resolution problem? What output do you get for console.debug(window.Block, window.Department)? If you do get the model type, it might help to give the relatedModel as a string, e.g. relatedModel: "Department".
Looks it's solved. Problem was in one line of code...
Backbone.Model.prototype.idAttribute = "Id"
I forgot i am using PascalCase identifiers for object properties. Everything looks ok for now.

Categories

Resources