Refresh $scope variables from a service - angularjs

What is the proper method of refreshing data that is retrieved from a service? I am retrieving a test array from my dataService that I'd like to be automatically updated in my view when my save() method is called. I have commented the line that is supposed to update my $scope variable but yet nothing changes. Should I be wrapping it in $apply()?
The data that is placed into the array that dataService.getActivities() returns is from a cookie (which may or not be relevant).
app.controller("activityCtrl", ["$scope", "dataService", function ($scope, dataService) {
$scope.newActivity = "";
$scope.activities = dataService.getActivities();
$scope.save = function (activity) {
try{
if (activity != "") {
dataService.saveActivity(activity);
$scope.newActivity = "";
$scope.activities = dataService.getActivities(); //HERE
}
} catch (e) {
alert(e);
}
}
}
Here is my view, the array lives in a ng-repeat
<div class="col-xs-12" ng-controller="activityCtrl">
<div class="row text-center">
<form novalidate>
<input type="text" placeholder="Activity name" ng-model="newActivity" />
<input type="submit" value="Add activity" ng-click="save(newActivity)" />
</form>
</div>
<div class="row" ng-controller="activityCtrl">
<div class="col-xs-2"> </div>
<div class="col-xs-6 text-left">
<div class="row" ng-repeat="activity in activities">
<div class="btn btn-default" ng-class="{ active : activity.active }" ng-click="activate(activity)">{{ activity.name }}</div>
</div>
</div>
<div class="col-xs-4 text-left">
sdfas
</div>
</div>
</div>
dataService code:
app.service("dataService", function () {
this.getActivities = function () {
if (docCookies.hasItem("activities")) {
var activities = JSON.parse(docCookies.getItem("activities"));
if (activities.constructor != Array) {
activities = [activities];
}
activities.sort(
function (a, b) {
if (a.name > b.name) { return 1; }
if (b.name < a.name) { return -1; }
return 0;
});
return activities;
}
return [];
}
this.saveActivity = function (activity) {
if (!docCookies.hasItem("activities")) {
docCookies.setItem("activities", [JSON.stringify({ "name": activity, "active": true })], Infinity);
} else {
var activities = this.getActivities();
for (var i = 0; i < activities.length; i++) {
if (activities[i].name == activity) {
throw "Activity already exists";
}
}
activities.push({ "name": activity, "active": false });
docCookies.setItem("activities", JSON.stringify(activities), Infinity);
}
};
});

Zac, your answer should work for you. As another option, I like to broadcast an event whenever my service is updated. In the last line of your saveActivity() function in your service, try broadcasting an event on the rootScope. Inject the $rootScope into your service. Add the following to your save method:
$rootScope.$broadcast('activitiesUpdated');
Then in your controller, inject the $rootScope and add an event handler:
$rootScope.$on('activitiesUpdated', function(event, args){
//Update $scope variable here
});

The answer was to look at the cookie value with a call to $watch instead of the method that returns an object. Added this in my activityCtrl
$scope.$watch(function () {
return docCookies.getItem("activities");
}, function (oldVal, newVal) {
$scope.activities = dataService.getActivities();
});

Related

Save the value of a service in a variable

I must fill a combo to be able to select the value that the user wanted I have managed to do this with the data stored in an array, the next step is to consult a service and use the return values
<html>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="text">ObjectType:</label>
<input type="text" ng-model="type" ng-keyup="complete(type)" class="form-control" placeholder="Search"/>
<ul class="list-group">
li class="list-group-item" ng-repeat="data in filterType" ng-click="fillTextbox(data)">{{data}}</li>
</ul>
</div>
</div>
</html>
var app = angular.module("Trace", []);
app.controller('TraceControlles', function ($scope, $http) {
//$scope.typeList = ["integration", "none"];
$scope.typeList = [];
$scope.loadTypes = function () {
$http.get("/api/Trace/GetObjectType")
.then(function (responce) {
$scope.typeList = responce.data;
}, function errorCallback(responce) {
});
}
$scope.complete = function (string) {
var output = [];
angular.forEach($scope.typeList, function (type) {
if (type.toLowerCase().indexOf(string.toLowerCase()) >= 0) {
output.push(type);
}
});
$scope.filterType = output;
}
$scope.fillTextbox = function (string) {
$scope.type = string;
$scope.filterType = null;
}
});
146/5000
When running the web api the service return values are not loaded, the browser does not mark any syntax error or anything like that.

Error while retrieving data from local storage

i am unable to retreive the data when i reopen my app.What should i do to permanently save the data
I tried this code:
<body ng-app="starter" ng-controller="Appctrl">
<form data-ng-submit="addTodo()" class="todo-form">
<input type="text" data-ng-model="todoText" placeholder="Enter new ToDo
item" />
<br />
<input type="submit" value="Add Task" />
</form>
<ul class="unstyled">
<li data-ng-repeat="item in todo track by $index">
<input type="text">
<span>{{ item.text }}</span>
</li>
</ul>
</body>
and in app.js:
.controller("Appctrl", function($scope) {
$scope.addTodo = function() {
$scope.text = $scope.todoText;
$scope.todos = [];
$scope.todo.push(text);
$scope.todoText = ''; //clear the input after adding
localStorage.setItem('todo', JSON.stringify($scope.todo));
$scope.saved = localStorage.getItem('todo');
localStorage.setItem('todo', JSON.stringify($scope.saved));
};
});
When retrieving the data from local storage check for existence then JSON.parse(localStorage.getItem('todo')) the get from local storage
In your case:
//you will need to parse the string to store it as an object
$scope.saved = JSON.parse(localStorage.getItem('todo'));
You can use the following factory:
yourApp.factory('$localStorage', ['$window', function($window) {
return {
store: function(key, value) {
$window.localStorage[key] = value;
},
get: function(key, defaultValue) {
return $window.localStorage[key] || defaultValue;
},
storeObject: function(key, value) {
$window.localStorage[key] = JSON.stringify(value);
},
getObject: function(key,defaultValue) {
return JSON.parse($window.localStorage[key] || defaultValue);
}
}
}]);
To use the factory you should inject it in your controller and use it like the following:
//to store
$localStorage.storeObject("sometoken", data);
//to retrieve
$localStorage.getObject("sometoken", "");
i solved my issue:
.controller("Appctrl",function($scope){
$scope.saved = localStorage.getItem('id');
$scope.id = JSON.parse($scope.saved);
localStorage.setItem('id', JSON.stringify($scope.id));
$scope.addTodo = function() {
//$scope.text=$scope.todoText;
$scope.id = [];
$scope.id.push({text:$scope.idText});
$scope.idText = ''; //clear the input after adding
localStorage.setItem('id', JSON.stringify($scope.id));
};
});
Check your code in controller.
You are doing : $scope.todo.push(text); instead of $scope.todos.push(text); itseems

Nested ng-repeat gives error after 10 levels deep: 10 $digest() iterations reached

Getting this error: Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
I have created a reddit style nested comment system using AngularJS. But after 10 levels deep i get a very ugly error on my console that looks like this:
This happens after exactly 10 levels deep:
The nested comments are directives that look like this:
commentCont.tpl.html:
<div class="comment-cont">
<div
class="upvote-arrow"
ng-show="!collapsed"
ng-click="vote()"
ng-class="{'active' : node.votedByMe}"
>
<div class="upvote-arrow-top"></div>
<div class="upvote-arrow-bottom"></div>
</div>
<div class="wrapper">
<div class="info">
<span class="collapse" ng-click="collapsed = !collapsed">[ {{collapsed ? '+' : '–'}} ]</span>
<span ng-class="{'collapsed-style' : collapsed}">
<a ui-sref="main.profile({id: node.userId})">{{node.username}}</a>
<span>{{node.upvotes}} point{{node.upvotes != 1 ? 's' : ''}}</span>
<span mg-moment-auto-update="node.createdTime"></span>
<span ng-show="collapsed">({{node.children.length}} child{{node.children.length != 1 ? 'ren' : ''}})</span>
</span>
</div>
<div class="text" ng-bind-html="node.comment | autolink | nl2br" ng-show="!collapsed"></div>
<div class="reply" ng-show="!collapsed">
<span ng-click="formHidden = !formHidden">reply</span>
</div>
</div>
<div class="input-area" ng-show="!collapsed">
<form ng-show="!formHidden" name="form" autocomplete="off" novalidate ng-submit="submitted = true; submit(formData, form)">
<div class="form-group comment-input-feedback-branch has-error" ng-show="form.comment.$invalid && form.comment.$dirty">
<div ng-messages="form.comment.$error" ng-if="form.comment.$dirty">
<div class="input-feedback" ng-message="required">Comment text is required.</div>
<div class="input-feedback" ng-message="maxlength">Comment text cannot exceed 2500 characters.</div>
</div>
</div>
<div
class="form-group"
ng-class="{ 'has-error' : form.comment.$invalid && form.comment.$dirty, 'has-success' : form.comment.$valid }"
>
<textarea
name="comment"
class="textarea comment-cont-textarea"
ng-model="formData.comment"
required
ng-maxlength="2500"
textarea-autoresize
></textarea>
</div>
<div class="form-group">
<mg-button-loading
mgbl-condition="awaitingResponse"
mgbl-text="Save"
mgbl-loading-text="Saving..."
mgbl-class="btn-blue btn-small"
mgbl-disabled="!form.$valid"
></mg-button-loading>
<mg-button-loading
mgbl-text="Cancel"
mgbl-class="btn-white btn-small"
ng-click="formHidden=true;"
></mg-button-loading>
</div>
</form>
</div>
<div ng-show="!collapsed">
<div ng-repeat="node in node.children" ng-include="'commentTree'"></div>
</div>
</div>
commentCont.directive.js:
(function () {
'use strict';
angular
.module('app')
.directive('commentCont', commentCont);
/* #ngInject */
function commentCont ($http, user, $timeout) {
return {
restrict: 'E',
replace: true,
scope: {
node: '='
},
templateUrl: 'app/post/commentCont.tpl.html',
link: function (scope, element, attrs) {
var textarea = element.querySelector('.comment-cont-textarea');
var voteOK = true;
var action = '';
var userInfo = user.get();
scope.formHidden = true; // Do not ng-init="" inside an ng-repeat.
scope.collapsed = false; // Do not ng-init="" inside an ng-repeat.
// Autofocus textarea when reply link is clicked.
scope.$watch('formHidden', function(newValue, oldValue) {
if (newValue !== true) {
$timeout(function() {
textarea[0].focus();
});
}
});
scope.submit = function (formData, form) {
if (form.$valid) {
scope.awaitingResponse = true;
formData.parentId = scope.node.id;
formData.postId = scope.node.postId;
formData.type = 4;
formData.fromUsername = userInfo.username;
formData.toId = scope.node.userId;
formData.fromImage = userInfo.thumbnail36x36.split('/img/')[1];
// console.log(formData);
$http.post('/api/comment', formData)
.then(function (response) {
scope.awaitingResponse = false;
if (response.data.success) {
if (response.data.rateLimit) {
alert(rateLimitMessage);
return false;
}
// id and createdTime is sent with response.data.comment.
var c = response.data.comment;
var newCommentNode = {
id: c.id,
userId: userInfo.id,
username: userInfo.username,
parentId: formData.parentId,
comment: formData.comment,
upvotes: 0,
createdTime: c.createdTime,
votedByMe: false,
children: [],
postId: scope.node.postId
};
// console.log('user', user.get());
// console.log('scope.node', scope.node);
// console.log('response', response);
// console.log('newCommentNode', newCommentNode);
formData.comment = '';
form.comment.$setPristine();
scope.formHidden = true;
scope.node.children.unshift(newCommentNode);
}
});
}
};
scope.vote = function() {
if (voteOK) {
voteOK = false;
if (!scope.node.votedByMe) {
scope.node.votedByMe = true;
action = 'add';
} else {
scope.node.votedByMe = false;
action = 'remove';
}
var data = {
commentId: scope.node.id,
action: action
};
$http.post('/api/comment/vote', data)
.then(function (response) {
// console.log(response.data);
voteOK = true;
if (action === 'add') {
scope.node.upvotes++;
} else {
scope.node.upvotes--;
}
});
}
};
}
};
}
}());
The tree is being called like this:
<script type="text/ng-template" id="commentTree">
<comment-cont
node="node"
></comment-cont>
</script>
<div ng-repeat="node in tree[0].children" ng-include="'commentTree'"></div>
How can I have more than 10 levels of nested ng-repeat without getting an error like this?
The default implementation of $digest() has limit of 10 iterations . If the scope is still dirty after 10 iterations error is thrown from $digest().
The below stated is one way of configuring the limit of digest iterations to 20.
var app = angular.module('plunker', [], function($rootScopeProvider) {
$rootScopeProvider.digestTtl(20); // 20 being the limit of iterations.
});
But you should look in to stabilizing your model rather than configuring the limit of iterations.
I was dealing with a similar issue. end up with the following directive:
(function () {
'use strict';
angular
.module('app')
.directive('deferDom', ['$compile', ($compile) => {
return {
restrict: 'A',
compile: (tElement) => {
// Find, remove, and compile the li.node element.
let $el = tElement.find( "li.node" );
let transclude = $compile($el);
$el.remove();
return function($scope){
// Append li.node to the list.
$scope.$applyAsync(()=>{
tElement.append(transclude($scope));
});
}
},
};
}]);
})();

md-checkbox does not work with ng-click

I want to save position each time when I change checkbox:
<h1 class="md-display-2">Simple TODO ng app</h1>
<h2 class="md-display-3"></h2>
<div ng-include src="'todo/add.html'"></div>
<div>
<div layout="row">
<div flex class="md-title">Scope</div>
<div flex="10" class="md-title">Till date</div>
<div flex="10" class="md-title">Is reached?</div>
<div flex="10" class="md-title">
<span ng-click="todoctrl.show_add()" class="material-icons controls">add</span>
</div>
</div>
<div layout="row" ng-repeat="todo in todoctrl.todos track by $index">
<div flex ng-class="{true:'striked', false:'simple'}[todo.reached]">{{todo.name}}</div>
<div flex="10">
{{todo.tillDate | date:'dd/MM/yyyy'}}
</div>
<div flex="10">
<md-checkbox ng-model="todo.reached" aria-label="Is reached" ng-click="todoctrl.changeState(todo.name)"></md-checkbox>
</div>
<div flex="10">
<span ng-click="todoctrl.deleteScope(todo.name)"
class="material-icons controls">clear</span>
</div>
</div>
</div>
In this case controller is touched (I tried with to debug with console log), but the checkbox value is not changed before page reload. After reload the value is checkbox is presented as expected.
If I remove ng-click="todoctrl.changeState(todo.name)" then checkbox is working good, but no info is sent to controller.
This is my service:
(function() {
'use strict';
angular.module('app').service('ToDoService', ToDoService);
ToDoService.$inject = ['JsonService'];
function ToDoService(JsonService) {
return {
deleteScope : deleteScope,
submitScope : submitScope,
changeState : changeState,
getData : getData
}
function getData() {
var todos = JsonService.getData();
return todos;
}
function deleteScope(arr, scope) {
arr.splice(findElementByScope(arr, scope), 1);
JsonService.setData(arr);
}
function submitScope(arr, scope, tillDate) {
var newTodo = {};
newTodo.name = scope;
newTodo.reached = false;
newTodo.tillDate = tillDate;
arr.push(newTodo);
JsonService.setData(arr);
}
function changeState(arr, scope) {
console.log("Service change state for scope: " + scope);
var todo = {};
var index = findElementByScope(arr, scope);
todo = arr[index];
todo.reached = !todo.reached;
JsonService.setData(arr);
}
function findElementByScope(arr, scope) {
for (var i = arr.length; i--;) {
if (arr[i].name == scope) {
return i;
}
}
return -1;
}
}
})();
And this is the Controller:
(function() {
'use strict';
angular.module('app').controller('ToDoController', ToDoController);
function ToDoController(ToDoService) {
var vm = this;
vm.show_form = false;
vm.todos = ToDoService.getData();
vm.scope = '';
vm.show_add = show_add;
vm.submitScope = submitScope;
vm.deleteScope = deleteScope;
vm.changeState = changeState;
function show_add() {
console.log("Controller show add");
vm.show_form = true;
}
function submitScope() {
ToDoService.submitScope(vm.todos, vm.scope, vm.tillDate);
vm.show_form = false;
vm.scope = '';
}
function deleteScope(scope) {
ToDoService.deleteScope(vm.todos, scope);
}
function changeState(scope) {
ToDoService.changeState(vm.todos, scope);
}
}
})();
Use ng-change instead of ng-click
<md-checkbox ng-model="todo.reached" aria-label="Is reached" ng-change="todoctrl.changeState(todo.name, todo.reached)"></md-checkbox>
ng-change trigger after value change in model

Scope binding not updating in http call

I'm having trouble updating my scope from inside this function updateRefresh(). The first iteration I can see that message == "test", and then it does overwrite what's in the scope, but what it is binding to on my HTML page, <span>{{refreshDomainStatus.message}}</span>, is still bound to the object where message="test"
I have tried $scope.$apply() but it says that $digest is currently in progress.
app.controller('AssessmentController', ['$scope', '$http', '$timeout', 'ConnectionService', function ($scope, $http, $timeout, connectionService) {
$scope.refreshDomainStatus = {
message: "test"
};
var updateRefresh = function(updateKey) {
$http.get('/assessment/api/update-refresh-domain/' + updateKey).success(function(response) {
$scope.refreshDomainStatus = response.refreshDomainStatus;
if (!response.refreshDomainStatus.halted) {
$timeout(function () { updateRefresh(updateKey); }, 250);
}
});
}
First Iteration:
Second Iteration:
After finishing and inspecting the context:
Shouldn't you set $scope.refreshDomainStatus to response.refreshDomainStatus?
$http.get('/assessment/api/update-refresh-domain/' + updateKey).success(function(response) {
$scope.refreshDomainStatus = response.refreshDomainStatus;
if (!response.refreshDomainStatus.halted) {
$timeout(function () { updateRefresh(updateKey); }, 250);
}
});
My problem was that I was creating two instances of the controller. I guess I want some sort of singleton, but for future reference, this is bad:
<div class="row" ng-controller="AssessmentController">
<div class="col">
<button class="btn btn-default" ng-click="refresh()">Refresh</button>
</div>
</div>
<div ng-show="" class="row" ng-controller="AssessmentController">
<div class="col">
<span>{{refreshStatus.message}}</span>
</div>
</div>

Resources