I'm struggling with angularjs directive templates. The {{variable}} works in a very strange way inside a ng-repeat,
<div ng-controller="MyCtrl">
<h2>here i am</h2>
<button type="button" ng-click="addItem()" class="btn btn-primary">Howdy</button>
<div ng-repeat="item in items track by $index" itemlist></div>
</div>
<script type="text/ng-template" id="template.html">
<div>
Howdy {{item.itemNum}} {{item.name}}
</div>
</script>
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function ($scope) {
$scope.count = 0;
$scope.items = [];
var newItem = {
itemNum: 0,
name: "New"
};
$scope.addItem = function () {
newItem.itemNum = $scope.count;
console.log('adding item ' + newItem.itemNum);
$scope.items.push(newItem);
$scope.count += 1;
};
});
myApp.directive('itemlist', function ($compile) {
return {
templateUrl: 'template.html',
};
});
I have created a jsfiddle showing my problem here: http://jsfiddle.net/dk253/8jm5tjvf/23/.
What am I missing or doing wrong?
Thanks!
The reason is you are updating the property on the same object reference (newItem) every time. So it updates all other items in the array because they all just point to the same object or in other words they are all same. You could instead get the copy of the object using angular.copy and push that item.
var item = {
itemNum: 0,
name: "New"
};
$scope.addItem = function () {
var newItem = angular.copy(item); //Get the copy
newItem.itemNum = $scope.count;
Fiddle
Related
I have a nested ng-repeat with a filter to group by.
I have created this
fiddle.
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl',['$scope', function($scope) {
$scope.data =[
{subject: 'English',module: 'Literature', score: 95},
{subject: 'Maths',module: 'Calculus', score: 90}
];
$scope.dropScore = function() {
if ($scope.data[0].score > 0) {
$scope.data[0].score -= 8;
}
}
$scope.origData = angular.copy($scope.data)
$scope.reset = function () {
$scope.data = angular.copy($scope.origData);
};
}])
.filter('groupBy', function() {
return _.memoize(function(items, field) {
return _.groupBy(items, field);
}
);
});
When you press the button hit score the score of English drops but clicking reset will reset the $scope.data value but not show the updated data on the screen.
Can someone help with this
Working Demo
Just use this reset method:
$scope.reset = function () {
$scope.data[0].score = angular.copy($scope.origData[0].score);
};
You need to call $scope.$apply() to refresh your scope :
$scope.dropScore = function() {
if ($scope.data[0].score > 0) {
$scope.data[0].score -= 8;
$scope.$apply();
}
}
http://jsfiddle.net/zewaeqpx/
When you do ng-repeat=(subject, value) in data | groupBy: 'subject', you are creating another array for ng-repeat to use. So either you would need to assign each parameter within or not use the filter in the ng-repeat.
Option 1:
If you want to keep how you are using the ng-repeat, you could to this:
$scope.reset = function () {
_.forEach($scope.data, function(subject, idx){
subject.score = $scope.origData[idx].score;
});
};
Option 2:
or you could simplify your ng-repeat so that it works like this where the template becomes:
<li ng-repeat="subject in data">
<span ng-bind="subject.subject"></span>
<ul>
<li ng-bind="subject.module + ' - ' + subject.score">
</li>
</ul>
</li>
You should use ng-bind because in some browsers, when the data attribute is not loaded, it will flash {{}} for a split second before it loads.
I've created a simple To Do App and while working on it I felt like I will end up placing too much code into my Controller and will eventually get messy and hard to read. I want to know how can I move my functions into factories so that my code can look somewhat cleaner.
Here is my JS:
angular.module('toDoApp', [])
.controller('toDoCtrl', function($scope){
//set $scope variables
$scope.tasks = [];
$scope.submitTask = function(){
$scope.tasks.unshift($scope.enteredTask);
$scope.enteredTask = '';
};
$scope.removeTask = function(task) {
var i = $scope.tasks.indexOf(task);
$scope.tasks.splice(i, 1);
};
})
.factory('toDoFactory', ['$http', function($http){
return function(newTask) {
};
}])
Here is the HTML if needed:
<form ng-submit="submitTask()">
<!-- task input with submit button -->
<label>Task: </label>
<input type="text" placeholder="Enter Task" ng-model="enteredTask" required>
<button>Submit</button>
</form>
<div>
<!-- create unordered list for task that are submitted
need check boxes -->
<ul>
<li ng-repeat="task in tasks">
{{ task }}
<button ng-click="removeTask()">x</button>
</li>
</ul>
</div>
As you can see I kinda started the factory but just don't know how to go about it.
Any suggestions will be greatly appreciated.
You will need to inject your factory inside controller and then use the methods defined in the factory from the controller:
angular.module('toDoApp', [])
.controller('toDoCtrl', function($scope, toDoFactory){
//set $scope variables
$scope.tasks = [];
$scope.submitTask = function(){
toDofactory.submittask(); //Just for demo.Passin your parameters based on your implementation
};
$scope.removeTask = function(task) {
var i = $scope.tasks.indexOf(task);
$scope.tasks.splice(i, 1);
};
})
.factory('toDoFactory', ['$http', function($http){
var methods = {};
methods.submittask = function(){
//your logic here
};
methods.removetask = function(){
//your logic here
}
return methods;
}])
var app = angular.module('toDoApp', []);
app.controller('toDoCtrl', function($scope, toDoFactory){
$scope.tasks = [];
toDoFactory.get = function(){
}
toDoFactory.delete = function(){
}
toDoFactory.update = function(){
}
});
app.factory('toDoFactory', ['$http', function($http){
var todo = {};
todo.get = function(){
};
todo.delete = function(){
};
todo.update = function(){
}
return todo;
}]);
This is simple architecture, you can add more logic,
Make sure you know about dependency injection(DI)
Here is the answer for those that want to see what the end result will look like when all the code is plugged in. Thanks again for the answers as it was able to guide me in the right direction.
.controller('toDoCtrl', function($scope, toDoFactory){
$scope.tasks = toDoFactory.tasks;
$scope.submitTask = function(){
toDoFactory.submitTask($scope.enteredTask);
$scope.enteredTask = '';
};
$scope.removeTask = function(task) {
toDoFactory.removeTask();
};
})
.factory('toDoFactory', ['$http', function($http){
var toDo = {
tasks: [],
enteredTask: '',
submitTask: function(task){
toDo.tasks.unshift(task);
},
removeTask: function(task) {
var i = toDo.tasks.indexOf(task);
toDo.tasks.splice(i, 1);
}
};
}])
I have table with item list and modal window where i can change drug properties. When properties changed that drug have to remove from that list. But it's didn't remove.
Modal window:
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: $scope
}).result.then(function(data){
var index = _.findIndex($scope.drugs, {_id: data._id});
$scope.drugs.splice(index,1);
}
i think element didn't remove 'cause when i open modal window i create copy of my scope and then work with copy..
On that form i have refresh button that refresh all list.
$scope.refresh= function() {
$scope.drugs = UnresolvedDrugsService.query();
};
and it's didn't work too. I think it happens because i work with copy too.
Okey, i try to emit some event
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: $scope
}).result.then(function(data){
var index = _.findIndex($scope.drugs, {_id: data.data._id});
$rootScope.$emit('refreshDrug', index);
}
$rootScope.$on('refreshDrug', function(index){
$scope.drugs = [];
$scope.drugs.splice(index,1);
// $scope.drugs= UnresolvedDrugsService.query();
});
And it's not working.
Can you help me and describe what i doing wrong, thx!
UPD
modal window html
<form role="form" name="resolveDrugForm" ng-submit="saveResolvedDrug(drug) && $close(drug)">
........{some code, input's and label}......
<input type="submit" class="btn btn-primary" value="{{'COMMON.SAVE' | translate}}"/>
<button type="button" class="btn btn-default" ng-click="$dismiss()" >{{'COMMON.CANCEL' | translate}}</button>
code of ng-repeat
<tr ng-repeat="drug in drugs" ng-click="resolveDrug($index)">
<td>{{drug.productName || drug.description }}</td>
<td>{{drug.aiccode }}</td>
</tr>
and all method of controller:
$rootScope.$on('refreshDrug', function(index){
// $scope.drugs = [];
$scope.drugs.splice(index,1);
// $scope.drugs= UnresolvedDrugsService.query();
});
$scope.drugs= UnresolvedDrugsService.query();
$scope.refresh= function() {
$scope.drugs= UnresolvedDrugsService.query();
};
$scope.spliceEl = function(data){
var index = _.findIndex($scope.drugs, {_id: data._id});
$scope.drugs.splice(index,1);
return true;
};
$scope.saveResolvedDrug = function(drug){
DrugsService.addResolvedDrug(drug).then(function(data){
var index = _.findIndex($scope.drugs, {_id: data.data._id});
if(data.data.ingredients && data.data.ingredients.length > 0){
data.data.ingredients = JSON.parse(data.data.ingredients);
}
$scope.drugs.splice(index,1);
return true;
});
return true;
};
$scope.resolveDrug=function(index){
$scope.drug={};
var drugs = $scope.drugs;
$scope.drug=angular.copy($scope.drugs[index]);
var scope=$scope;
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: $scope
}).result.then(function(data){
console.log($scope.drugs);
var index = _.findIndex($scope.drugs, {_id: data._id});
//$scope.drugs.splice(index,1);
console.log($scope.drugs);
$rootScope.$emit('refreshDrug', index);
}, function(data){
}).finally(function(data){
$scope.refresh();
});
}
I didn't know why it didn't works with events. But if saveDrug in modal result but sumbit form - work fine.
Now code looks like
$scope.resolveDrug=function(index){
$scope.drug={};
var drugs = $scope.drugs;
$scope.drug=angular.copy($scope.drugs[index]);
var scope=$scope;
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: scope,
resolve: {
drug: function () {
return $scope.drug;
}
}
}).result.then(function(data){
}, function(data){
}).finally(function(data){
});
}
$scope.saveResolvedDrug = function(drug){
DrugsService.addResolvedDrug(drug).then(function(data){
var index = _.findIndex($scope.drugs, {_id: data.data._id});
if(data.data.ingredients && data.data.ingredients.length > 0){
data.data.ingredients = JSON.parse(data.data.ingredients);
}
$scope.drugs.splice(index,1);
return true;
});
return true;
};
$scope.init = function() {
return $scope.items = basketService.items;
};
ng-repeat = "item in items"
And working with $scope.items + refresh $scope.items with broadcasting.
OR
$scope.getItems = function() {
return basketService.items;
};
ng-repeat = "item in getItems()"
Is copying basketService.items to $scope.items a must done or is it the same as getItems() (speed, memory...) ?
I don't believe there is a difference between them, and it is really just a matter of style. Take a look at this (very crude and contrived) fiddle I created: http://jsfiddle.net/digitalzebra/92gA6/
Here is the very messy controller from that:
angular.module("foobar", []).controller("lol", function($scope) {
$scope.items = ["loe", "le", "effe"];
var blah = ["leoele", "elelkej", "elfkjflkje"];
$scope.doStuff = function() {
$scope.items.push("eee", "eeelkjf");
};
$scope.getItems = function() {
return blah;
}
$scope.doOtherStuff = function() {
blah.push("elejlee'e");
};
});
And here is my HTML:
<div ng-app="foobar">
<div ng-controller="lol">
<b>First list:</b>
<div ng-repeat="item in items">
{{item}}
</div>
<b>Second list:</b>
<div ng-repeat="item in getItems()">
{{item}}
</div>
<button ng-click="doStuff()">Click me</button>
<button ng-click="doOtherStuff()">Do Other stuff</button>
</div>
</div>
Notice that both variants work, and both will give you two way binding etc.
How do I apply a filter to ngRepeat from a different controller?
From the Filters controller how do I applyFilter():
<div ng-controller="Filters">
<span ng-click="applyFilter()"></span>
</div>
... To items in the Results controller
<div ng-controller="Results">
<div ng-repeat="item in items">
{{item.thing}}
</div>
</div>
You could use either a parent controller or a service. The first example below uses a parent controller to define the items and filter value to share it with the two child controllers. The second example demonstrates the use of a service that is injected to both controllers and doesn't require a parent controller.
Parent Controller example: I would have one parent controller for both and define the data and filter value in that controller. This will allow each child controller to easily process and manipulate the filter:
Demo: http://plnkr.co/edit/yrtsX5SQsRiNSho6o9x8?p=preview
HTML:
<body ng-controller="MainCtrl">
Hello {{name}}!
<div ng-controller="Filters">
<span ng-click="applyFilter()">Apply Filter</span>
</div>
<div ng-controller="Results">
<div ng-repeat="item in items | filter:filterFunc ">
{{item}}
</div>
</div>
</body>
Controllers:
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.items = [1,2,3,4,5,6,7];
$scope.filterVal = 0;
});
app.controller('Filters', function($scope) {
$scope.applyFilter = function() {
$scope.$parent.filterVal = 5;
$scope.$apply();
}
});
app.controller('Results', function($scope) {
$scope.filterFunc= function(item) {
console.log(item);
if (item>$scope.filterVal)
return item;
}
});
Services Example. Here is an updated example with a service which contains the filter value and the items. http://plnkr.co/edit/wZFKBMRv0SeEsXNqARe2?p=preview
app.controller('Filters', function($scope, Utils) {
$scope.applyFilter = function() {
Utils.setFilter(4);
}
});
app.controller('Results', function($scope, Utils) {
$scope.items = Utils.getItems();
$scope.filterFunc= function(item) {
console.log(item);
if (item>Utils.getFilter())
return item;
}
});
angular.module('ServicesUtils', []).
factory('Utils', [ function () {
var items = [1,2,3,4,5,6,7];
var filter = 0;
var service = {
getFilter:function() {
return filter;
},
getItems:function() {
return items;
},
setFilter:function(n) {
filter = n;
}
};
return service;
}]);