why $scope change the value in the whole page? - javascript

I'm learning AngularJS and I'm trying to make a todo app.
Everything works great except that when I'm trying to add a new todo, the previous todos changing as well.
I think it's because the $scope changing its value in the whole page and I want to chnage it only in the last todo which just generated.
Maybe my code would be wrong for this purpose, again, I just started learning AngularJS.
Hope you can help me out, here is my code:
var myApp = angular.module('myApp', ['ngRoute']);
myApp.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'pages/main.html',
controller: 'mainController'
})
.when('/todo', {
templateUrl: 'pages/todo.html',
controller: 'subController'
})
});
myApp.controller('mainController', ['$scope', function($scope){
}]);
myApp.controller('subController', ['$scope', '$compile', function($scope, $compile){
$scope.getMsg = function(){
$scope.todoHeader = $('#header').val();
$scope.todoMsg = $('#msg').val();
var item = $compile("<todo-item todo-Title='{{todoHeader}}' todo-message='{{ todoMsg }}'></todo-item>")($scope);
$(".list-group").append(item);
}
}]);
myApp.directive('todoItem', function(){
return {
templateUrl: 'directives/todoItem.html',
scope: {
todoTitle: "#",
todoMessage: "#"
}
};
});
<h3>Todo Page</h3>
<div style="margin:auto;margin-bottom: 10px;display:table;">
<input type="text" id="header" placeholder="Enter todo header" style="margin-right:10px;padding:5px;"><br>
<textarea type="text" id="msg" placeholder="Enter todo message" style="margin-right:10px;padding:5px;"></textarea><br>
<button type="button" class="btn btn-primary" ng-click="getMsg()">Add Todo</button>
</div>
<div class="list-group" style="margin: auto; display: table;">
</div>
Here is the directive (todoItem.html) code:
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start" style="width:600px">
<div class="d-flex w-100 justify-content-between">
<h1 class="mb-1">{{ todoTitle }}</h1>
</div>
<p class="mb-1">{{ todoMessage }}</p>

Yes indeed in your getMsg function you are always overriding the same $scope todoHeader and todoMessage variables .
And this is the default behaviour of a variable in the $scope, if a variable is declared in the $scope will be shared in the whole application, so it's changed it will affect all it's occurences in the pages.
Solution:
I think you should store your todos in an array in your scope and each time push a todo item into it, or just make the two todoHeader and todoMessage local to your getMsg function and use them in your new HTML todo item.
This is how would be your code:
//If you wanted to store the todos in an array
$scope.todos = [];
$scope.getMsg = function() {
var todoHeader = $('#header').val();
var todoMsg = $('#msg').val();
//or if you want to store the todos in an array
$scope.todos.push({
todoHeader: todoHeader,
todoMessage: todoMessage
});
var item = $compile("<todo-item todo-Title='+todoHeader+' todo-message='+todoMessage+'></todo-item>")($scope);
$(".list-group").append(item);
}

Related

Getting ID of the first item only on using ng-repeat

So here's my workflow-
I've got an HTML file in which a div tag is created on which I've placed ng-repeat which iterates and gives me a list of items. On this div tag, I've placed an ng-click function. On clicking and item in the div tag, a modal-popup is opened.
What I need is to pass the id of the item from ng-repeat and show the data of this id in the modal-popup.
Now I've written the code upto here and all things are working fine, but the issue that I'm facing is if I click on any of the items from ng-repeat the first item is only returned, and hence data for the id of the first item is only being displayed in the modal-popup.
How could I get the id of the particular item clicked (and not the first item) and pass it to the controller?
Here's my working code -
main HTML:
<div id="main">
<div ng-repeat="data in JsonData" ng-click="openModal()">
<div id="widget">
<div id="{{$index}}">
<div>
<h2 class="font-bold no-margins" id="{{data.itemName}}">
{{data.itemName}}
</h2>
</div>
<div>
// other code
</div>
</div>
</div>
</div>
</div>
main controller.js:
$scope.openModal = function () {
$rootScope.elementid = document.getElementById('main').getElementsByTagName('div')[2];
$rootScope.variableId = $scope.elementid.id; // This gives the value in {{$index}}
$rootScope.elementname = document.getElementById('main').getElementsByTagName('h2')[0];
$rootScope.variablename = $scope.elementname.id; // This gives the value in {{data.itemName}}
$uibModal.open({
templateUrl: 'url/to/modal/popup.html',
controller: 'PopUpController',
scope : $scope,
windowClass: "animated fadeIn",
backdrop:'static'
});
};
On doing inspect element, I found that the elements are getting their correct id.
This is for the {{itenName}} code: (names are coming correct)
h2#CorrectName.ng-binding
and this is for the {{$index}} code: (here, id is incrementing for the items of ng-repeat)
div#0.ng-binding
So where am I wrong here? Is it due to any asynchronous call? Or is it due to ng-binding (i.e id of the item is returned before the ng-binding function completes)?
I'm really stuck here for a couple of days now. Any help would be much appreciated. Thanks.
You should not get the HTML data, instead you should pass the values to your function
ng-click="openModal(data)"
and from that on you can get the data in your funtion
$scope.openModal = function (data) {
and now you can do with that data whatever you want
console.log(data.itemName)
angular.module('test', []).controller('test', function($scope) {
// Test data
$scope.JsonData = [{itemName: "Test"}, {itemName: "OtherTest"}];
$scope.openModal = function(data) {
// handling data
console.log(data);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="test">
<div ng-repeat="data in JsonData" ng-click="openModal(data)">
<div id="widget">
<div id="{{$index}}">
<div>
<h2 class="font-bold no-margins" id="{{data.itemName}}">
{{data.itemName}}
</h2>
</div>
</div>
</div>
</div>
</div>
you can pass your $index to ng-click="openModal()" , so it will be ng-click="openModal($index)" .
controller
$scope.openModal = function (id) {
console.log(id); // log the clicked id
}
you can pass selected JsonData object as parametr of openModal function
<div ng-repeat="data in JsonData" ng-click="openModal(data)">
also you can pass selected obj to modal controller
$scope.openModal = function (selectedObj) {
$uibModal.open({
templateUrl: 'url/to/modal/popup.html',
controller: 'PopUpController',
scope : $scope,
windowClass: "animated fadeIn",
backdrop:'static',
resolve : {
selected: function () {
return selectedObj;
}
}
});
};
and get selected obj in PopUpController
app.contoller('PopUpController',['selected', function(selected){
console.log(selected)
}])

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
}

How to display the tick in items checked in the checkbox after reload?

I am trying to save the checked items in the checkbox and display them. I can store them locally and display. However the tick is not stored. Every time i reload the page the checked item that is stored is displayed but the tick is missing. Any idea on how to also store the tick in checked items and display them in the checkbox?
jsfiddle code I am currently trying is : https://jsfiddle.net/bhgmw7ey/
the html code is
'
<div ng-repeat="chip in colores" >
<input type="checkbox" name="{{chip.codigo}}" id="{{chip.codigo}}" ng-model="chip.checked" ng-change="chipsColores()" ng-click="$storage.a = fav">
<label>{{chip.codigo}}</label>
</div>
<div ng-repeat="favorite in $storage.a">
localStorage: {{favorite.codigo}}
</div>
</div>
'
the javascript code is
(function() { angular.module('myApp',['ngStorage']) .controller('favCtrl',[ '$scope', '$filter', '$localStorage', function ( $scope, $filter, $localStorage) {
$scope.colores = [
{'nombre':'blue', 'codigo':'1111'},
{'nombre':'green', 'codigo':'2222'},
{'nombre':'red', 'codigo':'3333'} ];
$scope.chipsColores = function () {
$scope.fav = $filter('filter')($scope.colores, {checked: true});} $scope.$storage = $localStorage.$default({ });}])})();'
your ngStorage is not properly used referring to the official wiki of this library https://github.com/gsklee/ngStorage.
Following the get started guide u should implement the localstorage by bind it to a scope variable so that it can watch and update the variables in localstorage for u.
For ur case it should look like
<div ng-app="myApp">
<div ng-controller="favCtrl">
<div ng-repeat="chip in $storage.colores" >
<input type="checkbox" name="{{chip.codigo}}" id="{{chip.codigo}}" ng-model="chip.checked" ng-change="chipsColores()" >
<label>{{chip.codigo}}</label>
</div>
<div ng-repeat="favorite in $storage.colores">
localStorage: {{favorite.codigo}}
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ngStorage/0.3.6/ngStorage.min.js"></script>
And JS:
angular.module('myApp',['ngStorage'])
.controller('favCtrl',[ '$scope', '$filter', '$localStorage', function ( $scope, $filter, $localStorage) {
$scope.chipsColores = function () {
$scope.fav = $filter('filter')($scope.$storage.colores, {checked: true});
}
//move the 'colores' into storage
$scope.$storage = $localStorage.$default({
colores : [
{'nombre':'blue', 'codigo':'1111'},
{'nombre':'green', 'codigo':'2222'},
{'nombre':'red', 'codigo':'3333'}
]
});
}])

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.

How to access data from multiple instances of the same child controller?

I have two types of controllers:
The first type of controller only appears once on an html page ("SingleChildController").
The second type of controller appears twice on the same page ("MultipleChildController") and is differentiated by the value of its property "instance".
Both of these are nested within my ParentController (see code below).
How can I access data from any of my 3 child controller instances in the ParentController?
I have tried adding two services, one for the SingleChildController and one for the MultipleChildController, but it seems like by doing so I am duplicating a lot of code. This is especially true in my actual code where each controller has many properties to monitor. Is there a better way to accomplish this?
EDIT: For further clarification, this view is for a form and I want to get all of my data back up into the ParentController so that I can submit my form.
(function() {
var app = angular.module('myApp', []);
app.controller('ParentController', ['$scope',
function($scope) {
var parent = this;
parent.singleName = "";
parent.multiple1Name = "";
parent.multiple2Name = "";
}
]);
app.controller('SingleChildController', ['$scope',
function($scope) {
var single = this;
single.name = "";
}
]);
app.controller('MultipleChildController', ['$scope',
function($scope) {
var multiple = this;
multiple.instance = "";
multiple.name = "";
}
]);
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="ParentController as parent">
<!-- I would like these to show the data that was inputted below -->
ParentSingleName: {{parent.singleName}} <br />
ParentMultiple1Name: {{parent.multiple1Name}} <br />
ParentMultiple2Name: {{parent.multiple2Name}} <br /><br />
<div ng-controller="SingleChildController as single">
<input type="text" ng-model="single.name" />: {{single.name}}
</div>
<div ng-controller="MultipleChildController as multiple1" ng-init="multiple1.instance='first'">
<input type="text" ng-model="multiple1.name" />: {{multiple1.instance}} - {{multiple1.name}}
</div>
<div ng-controller="MultipleChildController as multiple2" ng-init="multiple2.instance='second'">
<input type="text" ng-model="multiple2.name" />: {{multiple2.instance}} - {{multiple2.name}}
</div>
</div>

Categories

Resources