Angular : How to load images through XHR request in ng-repeat loop - angularjs

I am trying to create a contact book in which every person can have a photo.
As I would like to display the contact before getting its photo, I would write something like
<ul>
<li ng-repeat="person in persons">{{person.firstname}} {{persone.lastname}}</li>
</ul>
I would try to call a get_photo(person) available in my scope.
<ul>
<li ng-repeat="person in persons">{{person.firstname}} {{persone.lastname}}
<img ng-src="{{get_photo(person)}}" />
</li>
</ul>
Which doesn't work because it binds the function to ng-src and call get_photo every time ng-repeat is called.
I finally tried to call get_photo when initializing the DOM element and store the result in a specific attribute photo
<ul>
<li ng-repeat="person in persons">{{person.firstname}} {{persone.lastname}}
<img ng-init="get_photo(person)" ng-src="{{person.photo}}" />
</li>
</ul>
$scope.get_photo = function (person) {
// Simple example; I will try to get an image through xhr request later...
person.photo = "http://img.blogduwebdesign.com/benjamin-sanchez/737/AngularJS.jpg";
};
Is there an easier / cleaner way to do the same ?
I also created a jsFiddle : http://jsfiddle.net/V4Mss/
Thanks a lot for your feedback !
t00f

Your last example seems fine but here is another option that keeps the view a little cleaner and doesn't cause the double $digest. Remove the initialization function from each element and use the function as a getter.
HTML
<img ng-src="{{person.photo}}" />
JavaScript
myapp.controller('controller', function ($scope) {
$scope.count = 0;
$scope.get_photo = function (person) {
return "http://img.blogduwebdesign.com/benjamin-sanchez/737/AngularJS.jpg";
};
$scope.persons = [{firstname:"toto", lastname:"TOTO", photo:$scope.get_photo()},
{firstname:"tata", lastname:"TATA", photo:$scope.get_photo()},
{firstname:"titi", lastname:"TITI", photo:$scope.get_photo()}];
});
Here is a fiddle

Related

List items not dragging using Sortable.js

Sortable.Js newbie here, so please be gentle.
I'm trying to implement a sortable list in my page using Sortable.js. I'm not getting any errors upon execution, however when I tried to drag my list items, they're not moving at all.
Here's my HTML:
<ul id="forcedranking" class="wizard-contents-container-ul forcedRankCls" ng-show="isForcedRankingQuestion">
<div class="answers-container">
<div class="answer-child-container">
<li ng-repeat="query in currentQuestionObject.choices | orderBy:['sequence','answer']" class="listModulePopup forcedRankingChoice" data-index="{{$index}}" ng-model="query.value">
{{query.answer}}
</li>
</div>
</div>
</ul>
And here's my JS:
/* Get current input type for Clear All checkbox activation */
$scope.currentInputType = $scope.currentQuestionObject.inputType.toLowerCase();
if ($scope.currentInputType == "forced ranking") {
$scope.isForcedRankingQuestion = true;
/* SORTABLE FOR FORCED RANKING */
var mySort = document.getElementById("forcedranking");
// forcedranking is my id for the <ul>
var sortable = Sortable.create(forcedranking, {});
// Tried setting this because I thought this was the culprit. Turns out it's not.
sortable.option("disabled", false);
I called my Sortable.js like so (underneath my angularJS libraries):
<script type="text/javascript" src="content1/carousel/Sortable.js"></script>
Overall, I think Sortable's a really neat library, which is why I want to make it work so bad. But I just don't know what I'm doing wrong here.
The problem is you are not following the sortable angular documentation. They recently change the project and separated the js from the frameworks.
So first you need to include the lib and the angular sortable lib angular-legacy-sortablejs
Sortable.js
ng-sortable.js
Second inject the module 'ng-sortable' in your app
Then you can pass the options (if you need to) in the html via directive or use it in the controller
HTML:
<ul ng-sortable="{group: 'foobar'}">
<li ng-repeat="query in currentQuestionObject.choices">{{query.answer}}</li>
</ul>
Or you can pass an object declared in your controller with the options, ex:
$scope.sortableConfig = {
group: 'collection',
animation: 150,
handle: '.handle',
filter: '.inbox'
}
<ul ng-sortable="sortableConfig">
<li ng-repeat="collection in test">
<div class="handle"></div>
{{collection.name}}
</li>
</ul>
Hope this help you!
I think what's going on here is that your JS is being executed before Angular is done rendering all the LI items, but it's hard to tell from your snippet of JS.
Just for testing, see if the items become draggable after 1 second if you change these two lines:
/* SORTABLE FOR FORCED RANKING */
var mySort = document.getElementById("forcedranking");
// forcedranking is my id for the <ul>
var sortable = Sortable.create(forcedranking, {});
into this:
window.setTimeout(function() {
/* SORTABLE FOR FORCED RANKING */
var mySort = document.getElementById("forcedranking");
// forcedranking is my id for the <ul>
var sortable = Sortable.create(forcedranking, {});
}, 1000);
Also, I don't believe that <div> elements are valid children for <ul> elements, so that may also be your problem. If the Javascript change does nothing, then perhaps you should try to change your html so that your <li> items are direct children of the <ul> element.
Hope this helps.

angularjs: how to invoke controller based on his parent results

I'm moving my very first steps with angularjs.
I have a controller which does a call to a service, which returns a list of urls (parent).
I would like to render a html ul for which each li is rendered by another controller (children) with its own template. I imagine something like this:
<ul ng-controller="ListCtrl">
<li ng-repeat="element in elements">
<div ng-controller="DetailCtrl">
{{oneField}} - {{anotherField}}
</div>
</li>
</ul>
The first controller is easy to implement:
myApp.controller('ListCtrl', function ($scope, $http) {
$http.get('services/elements')
.success(function(data) {
$scope.elements = data;
});
});
But for the second I have a problem, since I can't figure out how the controller could know which url use for the ajax call (DYNAMYC_URL):
myApp.controller('DetailCtrl', function ($scope, $http) {
$http.get(DYNAMYC_URL)
.success(function(data) {
$scope.element = data;
});
});
Could you please suggest me which is the best way in angular to approach this problem?
I've also considered to do many calls inside the first controller, but it doesn't seem a clean solution to me.
We will try to maintain the state of childRenderUrl function call in element while
initializing the element itself in ng-unit attribute.
Instead of DetailCtrl
We write a function renderChild defined as
$scope.renderChild =function(element)
{
$http.get(element.url)
.success(function(data) {
// add all required fields to element here
element.otherField=data.otherField;
element.sometherField=data.sometherField;
//a $scope.$digest may be required here
});
}
Your display logic would be
<ul ng-controller="ListCtrl">
<li ng-repeat="element in elements">
<div ng-init="renderChild(element)">
{{element.oneField}} - {{element.anotherField}}
</div>
</li>
</ul>
On initializing we would call the renderChild method which would
add two properties to the element object that would be aptly displayed by
expressions {{element.oneField}} - {{element.anotherField}}

Error: $update is not a function

I have nested tree pages in the mean.io angular controller from mongo db:
{"_id":"54a67318250272254669fa93","name":"test page 1","parent":null,"active":true,"children":[{"_id":"54a67326250272254669fa94","name":"test page 1.1","parent":"54a67318250272254669fa93","active":false,"children":[{"_id":"54a67330250272254669fa95","site":"54a6730a250272254669fa92","name":"test page 1.1.1","parent":"54a67326250272254669fa94","active":false,"children":[]}]}]}
Then I show it like this:
<ol>
<li data-ng-repeat="page in pages" data-ng-include="'renderer.html'">
</li>
</ol>
and it is a renderer.html:
<input type="checkbox" data-ng-model="page.active" data-ng-change="pageActive(page)" />
<ol data-ng-model="page.children">
<li data-ng-repeat="page in page.children" data-ng-include="'renderer.html'">
</li>
</ol>
this is code in the controller
$scope.pageActive = function (page) {
if (!page.updated) {
page.updated = [];
}
page.updated.push(new Date().getTime());
page.$update();
};
ok, my proplem:
then I click to checkbox and call pageActive function - I have error in the firebug (because nestet object has no update function):
Error: page.$update is not a function
but then i click to checkbox not in the nestet pages - it works
because my nested page model is overwriting parent model
Try changing
<li data-ng-repeat="page in page.children" data-ng-include="'renderer.html'"></li>
To
<li data-ng-repeat="childPage in page.children" data-ng-include="'renderer.html'"></li>
I don't know what you are referring to with page, but by using data-ng-repeat="page in page.children" you are overwriting whatever it is with the ng-repeat child, which does not have the function $update (hence it's undefined).

AngularJS , small trick

I've got one question . Is there anybody how has idea how to invoke function in controller only for the first li object in ul when ul has a ng-repeat?
Lets say ive got function in my controller like:
var init = function () {
var a = this;
};
And my ul repeater seams like lets say:
<ul ng-repeat="up in userpartners" ng-click="setSelected(this);">
<li>
{{up.id}}
</li>
</ul>
And I want to invoke init function with parameter 'this' which will be first 'li' item. Only for the first li i want to set something.
You can do it like this:
<ul ng-repeat="up in userpartners" ng-init="init(up, $first);">
<li>
{{up}}
</li>
</ul>
And in the controller:
$scope.init = function (item, isFirst) {
if (isFirst) {
alert(item);
}
};
Fiddle Example
See here check if $index is zero or $first is true.
But there's already a single LI inside your UL.
I believe you mean:
<ul>
<li ng-repeat="up in userpartners" ng-init="$first && setSelected(up)">
{{up.id}}
</li>
</ul>
or
<ul ng-init="setSelected(userpartners[0])">
<li ng-repeat="up in userpartners">
{{up.id}}
</li>
</ul>
See the difference? We want to repeat the children, not the whole list.
Although I believe it wouldn't be a good practice to have this call on a view. I'd put it inside a controller, the call itself.

Angularjs How to update partial template

I would like to know how to make controller function work whenever I change the state.
This is the main template of my Angular app:
<div>
<div ng-include="'/navigation.html'"></div>
<div>
<!-- Page wrapper -->
<div ng-include="'/topnavbar.html'"></div>
<!-- Main view -->
<div ui-view></div>
<!-- Footer -->
<div ng-include="'/footer.html'"></div>
</div>
</div>
In navigation panel I have ng-repeat:
<ul ng-controller="NavigationCtrl" class="nav nav-second-level">
<li ng-repeat="user in users" ui-sref-active="active">
<a ui-sref="account">{{user.name}}</a>
</li>
</ul>
So, whenever I change the state, I would like to update this scope var users.
I think this is well known problem, but I am new in Angular so help me please.
Well, I see two ways here:
1) Listening for state events, and do callback that updates user data. Docs here: https://github.com/angular-ui/ui-router/wiki#state-change-events
2) I assume that users array comes from service, something like:
...
$http.get('/users', function(data){
return data;
});
...
so, instead of this, you can store an array locally in service(which is singleton), and update it from any places, it that way you'll have a link to array in 'NavigationCtrl', and data will be updated there as well.
What I mean:
function UserService($http) {
var users = []; //inital array
var updateUsers = function() {
//get to update users here
};
var getUsers = fucntion() {
return users;
}
return {
updateUsers : updateUsers, //this method will be called when you need to update user list
getUsers : getUsers //this method will be called in NavigationCtrl
}
}
Update:
Here what I meant in pt.2 http://plnkr.co/edit/U2XwXNEqAxRrEa0VpYAu?p=preview

Resources