AngularJS : After ng-repeat $scope values not updated as expected - javascript

I have a controller that contains data to be iterated over with an ng-repeat, and I'm using it alongside this selection model directive.
After using ng-repeat to create several entries in the DOM, the bindings to $scope.selectedItems stop working.
I know ng-repeat create a new child scope for each iteration, but shouldn't the updates to the selectedItems property still be picked up by binding to values in the MyController?
Controller:
(function() {
angular.module('myModule', ['product.module'])
.controller('MyController' ['$scope', 'SomeService' function($scope, ProductService) {
$scope.selectedItems = [];
$scope.productService = new ProductService() //constructor pattern object
$scope.products = $scope.productService.items;
$scope.productService.fetchData(); //grabs data from server and sets the productService.items to an array of objects
}]);
})();
HTML
<div ng-controller="MyController">
<ul>
<li ng-repeat="product in products"
selection-model-type="checkbox"
selection-model-mode="multiple"
selection-model-selected-items="selectedItems">
{{product.name}}
</li>
</ul>
<div ng-controller="MyController"> <!-- this binding doesn't work after the ng-repeat takes place -->
There are {{selectedItems.length}} products
</div>

productService.items may refer to the same instance all the time but selectedItems surely not. Use only one controller or place selectedItems into a service.
<div ng-controller="MyController">
<ul>
<li ng-repeat="product in products"
selection-model-type="checkbox"
selection-model-mode="multiple"
selection-model-selected-items="selectedItems">
{{product.name}}
</li>
</ul>
There are {{selectedItems.length}} products
</div>

Related

Angular: passing index of object array from ng-repeat to access object data

I have an array of objects displayed with ng-repeat:
<div class = "row" ng-repeat="article in academicArticles">
<div class = "col-md-9 academicArticle" ng-click = "index = $index">
<a ui-sref ="article">
<h2> {{article.title}} </h2>
<p> {{academicArticles.indexOf(article)}} </p>
<img src = "{{article.img}}" class = "img-responsive">
</a>
</div>
</div>
I want to display the selected "article" in another state. Right now I have:
<div class ="container">
<div class = "jumbotron">
<img src = "../../../photos/danielpark.jpg">
<h1>{{academicArticles[index].title}}</h1>
<p> Written By: {{academicArticles[index].author}} </p>
</div>
</div>
No errors but {{academicArticles[index].author}} and {{academicArticles[index].title} are blank. How can I access the data of the selected article in another sate?
you can do that by passing the index of the selected "article" as state parameter to the other state.
assume the other state called state2, you need to add the state parameter to the state in the router registration, for example, if you rigstered the new state like this, you can add the index as follow
$stateProvider.state("state2", {
url: '/state2/:index',
templateUrl:'state2.html',
controller: 'state2Controller'});
and then in state2Controller you can access the index parameter using stateParams service like this :
(function() {
angular
.module("app")
.controller("state2Controller", state2Controller);
state2Controller.$inject = ["$scope", "$stateParams"];
function state2Controller($scope, $stateParams){
$scope.index = $stateParams.index;
}
})()
I think you are trapped by ng-repeat scope issue, the trick is not bind primitive types to scope directly instead use some object like $scope.model and $scope.model.authorId

Angular Nested Controllers Access ng-repeat data

I have two controllers, one nested inside the other, both using ng-repeat to essentially list arrays of related data. I'd like to access one of the properties in the ng-repeat of the parent controller in the child controller. I'm pretty new to Angular and not sure how to get this working or if I'm approaching it the wrong way. Any guidance would be helpful.
HTML
<div class="container" ng-app="myApp">
<div class="task" ng-controller="TaskController as taskCtl" ng-repeat="task in tasks">
{{task.name}}
<ul>
<li ng-controller="AttachmentController as attachmentCtl" ng-repeat="attachment in attachments">{{attachment.name}}</li>
</ul>
</div>
</div>
JS
var app = angular.module('myApp', []);
app.controller('TaskController', ['$scope', function ($scope) {
$scope.tasks = [{name:'thing1', id: '123456'}, ... ];
}]);
app.controller('AttachmentController', ['$scope', '$http', function ($scope, $http) {
$scope.attachments = [];
$scope.init = function init() {
$http.get('/api/attachments&task_id=' + **HOW_DO_I_GET_TASK_ID_HERE** )
.then(function(response) {
$scope.attachments = response.data;
});
};
$scope.init();
}]);
I'd like to load the attachments as they relate to the tasks based on the task id for a given iteration through ng-repeat. Not sure if I'm going about this the wrong way.
Thanks
Although it would be better to use a ng-repeat with a filter on all the attachments with the given id. Since now you are calling the /api/attachments&task_id for each task iteration.
Or to send the list of attachments directly on the /api/tasks call. Therefor you could loop them instantly when looping the tasks, without the need of fetching them on each iteration.
A possible solution according to your code provided:
<div class="container" ng-app="myApp">
<div class="task" ng-controller="TaskController as taskCtl" ng-repeat="task in tasks">
{{task.name}}
<ul>
<li ng-controller="AttachmentController as attachmentCtl" ng-repeat="attachment in getAttachments(task.id)">{{attachment.name}}</li>
</ul>
</div>
</div>
app.controller('AttachmentController', ['$scope', '$http', function ($scope, $http) {
$scope.getAttachments = function(id) {
$http.get('/api/attachments&task_id=' + id)
.then(function(response) {
return response.data;
});
};
}]);
Something like this from the child controller should work:
HTML:
<div class="container" ng-app="myApp">
<div class="task" ng-controller="TaskController" ng-repeat="task in tasks">
{{task.name}}
<ul>
<li ng-controller="AttachmentController" ng-repeat="attachment in fetchAttachments(task)">{{attachment.name}}</li>
</ul>
</div>
</div>
JS
Child Controller:
This fetchAttachments will be called for every iteration of the parent ngRepeat. You will have to "return" the result of the Ajax call to this function for it to work.
$scope.fetchAttachments = function (task) {
// call ajax
// return the result of ajax
}

UI-Sortable with Angular filter for ng-repeat

It seems that the UI-sortable (https://github.com/angular-ui/ui-sortable) plug in does not play well if there is a filter in the list that is being ng-repeated by angular. The drag and drops do not work as intended.
This is shown by these two fiddles:
First: Has filter - the drag and drop will not stay in place.
http://jsfiddle.net/Lvc0u55v/2886/
HTML:
<div ng-controller="Controller">
<ul ui-sortable="sortableOptions" id="test" ng-model="items">
<li class="line" ng-repeat="item in items | filter: myFilter">
{{item.name}}
</li>
</ul>
</div>
JS:
var myApp = angular.module('myApp',['ui.sortable']);
myApp.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Superhero';
$scope.items = [
{name: 'TEST'},
{name: 'TEST2'},
{name: 'TEST3'},
{name: 'TEST4'},
{name: 'TEST5'},
{name: 'TEST6'},
{name: 'TEST7'},
{name: 'TEST8'}
]
$scope.myFilter = function (item) {
return item.name !== 'TEST';
};
$scope.sortableOptions = {
opacity: '0.8',
axis: 'y',
tolerance: 'pointer',
}
}]);
Second: Has no filter - works as intended.
JS SAME,
HTML:
<div ng-controller="Controller">
<ul ui-sortable="sortableOptions" id="test" ng-model="items">
<li class="line" ng-repeat="item in items">
{{item.name}}
</li>
</ul>
</div>
http://jsfiddle.net/Lvc0u55v/2887/
I will create an issue on their github repo as well, but if anyone has a work around that would be great!
In the docs you can see
Filters that manipulate the model (like filter, orderBy, limitTo,...) should be applied in the controller instead of the ng-repeat (refer to the provided examples).
This is the preferred way since it:
- is performance wise better
- reduces the chance of code duplication
- is suggested by the angularJS team
- it does not break the matching of the generated DOM elements and the ng-model's items
So yeah, don't do filtering in the view, it is not intended to work with ui-sortable.
For example:
JS:
$scope.filtered= $filter('filter')($scope.items, $scope.myFilter);
HTML:
<li class="line" ng-repeat="item in filtered">
{{item.name}}
</li>
you are passing an array in ng-model.
ngModel directory works like just another attribute like id which is accessible by angular.
and ngModel is mostly used in the tags which produces data, like input, textarea, select and other custom directives.
simply instead of accessing the data from id by using the general DOM identification like
$scope.variable = document.getElementById('someElement').value
angular uses ngModel directive to bind the data in both ways.

How to pass ng-model to controller

I have a very hierarchical data structure in JSON. I also have a re-usable (Django) template that gets bound to part of that structure. So, I need to know where in that structure I am for any given template. I am almost there:
http://jsfiddle.net/trubliphone/fd64rn3y/
The missing bit is being able to pass the current ng-model to a controller.
Here is some code (but the jsfiddle above shows more detail):
my_app.js:
var myApp = angular.module('myApp', []);
myApp.factory('$global_services', ['$http', function($http) {
var data = {};
$http.get("some_url", {format: "json"}) {
/* THIS REQUEST RETURNS A BIG CHUNK OF HIERARCHICAL JSON */
.success(function (data) {
data = data;
})
});
return {
getPathFromModel: a_clever_fn_i_wrote_that_returns_a_string(),
getModelFromPath: a_clever_fn_i_wrote_that_returns_a_model()
}
}]);
myApp.controller('MyController', ['$scope', '$attrs', '$global_services', function( $scope, $attrs, $global_services ) {
if ($attrs.currentModelPath) {
/* if you passed current_model_path, get the current_model too */
$scope.current_model_path = $attrs.currentModelPath;
$scope.current_model = $global_services.getModelFromPath($scope.current_model_path);
}
else if ($attrs.currentModel) {
/* if you passed current_model, get the current_model_path too */
$scope.current_model = $attrs.currentModel;
$scope.current_model_path = $global_services.getPathFromModel($scope.current_model);
}
}]);
my_template.html:
<div ng-app="myApp">
<div ng-controller="MyController" current_model_path="data">
{{current_model.name}}:
<ul ng-repeat="child in current_model.children">
<input type="text" ng-model="child.name"/> {{ child.name }}
<!-- LOOK: HERE IS A NESTED CONTROLLER -->
<div ng-controller="MyController" current_model="child">
{{current_model.name}}:
<ul ng-repeat="child in current_model.children">
<input type="text" ng-model="child.name"/> {{ child.name }}
</ul>
</div>
</ul>
</div>
</div>
The problem is in the "div" element for the nested controller; I pass the {{child}} ng variable as an attribute, but when the controller recieves it, it just interprets it as the JavaScript string "child". How can I pass the actual model object?
Thanks.
<div ng-controller="MyController" ng-init="data = child">
this will add an object to the inner scope named data.

AngularJs - binding passed in paramter to object from different controller

I'm having difficulties in solving this. What I'm trying to achieve is to update iterated objects which is passed in to a function in a different controller.
Here is my controllers -
angular.module('eatmapp.controllers', ['eatmapp.services'])
.controller('AppCtrl', function($scope) {
$scope.intoCart = function(item) {
if(item.type == 'variations'){
item = newItemObj;
}
}
})
.controller('BrowseCtrl', function($scope, dataService, $localstorage) {
dataService.getItems().then(function(returnData) {
$scope.items = returnData.products;
})
});
Here is my view -
<div ng-controller="BrowseCtrl">
<div class="list card product" ng-repeat="item in items" ng-click="intoCart(item)">
<div class="item item-text-wrap">
<span class="ifs-productcat" ng-repeat="category in item.categories">{{category}}<span ng-if="$index != item.categories.length - 1">,</span></span><br>
<h3>{{item.title}}</h3>
<h3>Rs.{{item.price}}</h3>
</div>
</div>
</div>
I need to update item object with newItemObject in iteration(ng-repeat) implemeted in template view after doing some condition check with method (intoCart) in another controller(AppCtrl). I'm fairly new to javascript programming and I'm looking for some help.
The problem I had was not able to get access to 'ng-repeat' child scope in controller.
I solved this using 'this.item' in controller rather than passing the whole object or index.
HTML -
<div class="list card product" ng-repeat="item in items" ng-click="intoCart()"></div>
Controller -
angular.module('eatmapp.controllers', ['eatmapp.services'])
.controller('AppCtrl', function($scope) {
$scope.intoCart = function() {
item = this.item; // current (ng-click) child scope of ng-repeat
if(item.type == 'variations'){
item = newItemObj;
}
}
})
Now, whenever I made changes to 'item' object, it automatically updates scope in view (ng-repeat).
Once way I like to handle this is by using services as setters and getters. The problem is you have to include the service with every controller that needs to access it, but if you don't have too many it's no big deal. So something like this:
.service('userFirstName', function() {
var userFirstNameProp;
return {
getter: function() {
return userFirstNameProp;
},
setter: function(value) {
userFirstNameProp = value;
}
};
})
Then you can call userFirstName.getter() or userFirstName.setter("John") as appropriate.

Categories

Resources