Prevent $watchCollection to be triggered on init - javascript

I have "$scope.postits" array which I want to be persisted at every change. So, I put $scope.$watchCollection on this element to listen for changes and save data. The problem is that $watch is triggered 3 times on page load (my test array has 3 entries).
How to prevent that ? What's wrong with my code ?
view:
<div ng-controller="postitController as postit" class="container animate-bottom">
<h2>Post-it !</h2>
<div class="btn-container">
<button ng-click="addPostit()" id="add-new-note" class="btn btn-primary">Add postit</button>
</div>
<div class="post-it-container">
<div ng-repeat="postit in postits" class="postit">
<a ng-click="removePostit(postit)" class="delete-postit glyphicon glyphicon-remove"></a>
<textarea ng-model="postit.content" ></textarea>
</div>
<div ng-if="postits.length==0" class="no-postit well lead">Keep cool and have a beer there's no postit here !</div>
</div>
</div>
JS controller :
app.controller('postitController', function($scope, $http, $timeout) {
$scope.postitsLoaded = false;
var storage = {
endpoint: "localhost/backend/ws.php",
get: function() {
$http({
method: 'GET',
url: this.endpoint
}).then(function successCallback(response) {
$scope.postits = response.data;
$scope.postitsLoaded = true;
console.log("init done") ;
}, function errorCallback(response) {
console.log(response);
});
},
save: function () {
$http({
method: 'POST',
url: this.endpoint,
data: "postits="+ angular.toJson($scope.postits),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function successCallback(response) {
console.log(response);
}, function errorCallback(response) {
console.log(response);
alert("error");
});
}
}
$scope.$watchCollection("postits", function (newValue, oldValue) {
if(newValue === oldValue || !$scope.postitsLoaded){
console.log("return") ;
return;
}
console.log("watch triggered") ;
storage.save();
});
$scope.addPostit = function() {
$scope.postits.push({id:100,content:"foobar"});
storage.save();
};
$scope.removePostit = function(postit) {
$scope.postits.splice($scope.postits.indexOf(postit), 1) ;
storage.save();
};
storage.get();
});

This is finally working with $watch and the third parameter set to true :
$scope.$watch("postits", function (newValue, oldValue) {
//this prevent $watch to be triggered on init
if(newValue === oldValue || oldValue === undefined ){
console.log("return") ;
return;
}
console.log("watch triggered") ;
console.log(oldValue);
console.log(newValue);
storage.save();
},true);
with that solution there is not need to use any flag.

Related

Textbox binding not working when page has loaded AngularJS [duplicate]

This question already has answers here:
How to get promise result using AngularJS
(2 answers)
Closed 3 years ago.
I'm trying to set textbox value based on the result of promise values when page has loaded. The values on textbox set only if I click on it and then out of it (just like blur event).
Controller
(function() {
'use strict'
var newPurchasingCardController = function($scope, $rootScope, $filter, $window, $location, $timeout, requestService) {
$scope.actionTitle = "";
$scope.gin = "";
$scope.fullName = "";
$scope.workEmail = "";
$scope.msg = "";
var getCurrentUserData = function () {
var dfd = new $.Deferred();
var queryUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/SP.UserProfiles.PeopleManager/GetMyProperties";
$.ajax({
url: queryUrl,
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
success: onSuccess,
error: onError,
cache: false
});
function onSuccess(data) {
dfd.resolve(data);
}
function onError(data, errorCode, errorMessage) {
dfd.reject(errorMessage);
}
return dfd.promise();
}
var _init = function () {
$scope.actionTitle = "New Card Request"
var promise = getCurrentUserData();
promise.then(function (data) {
$.each(data.d.UserProfileProperties.results,function(i,property){
if(property.Key === "EmployeeNumber")
{
$scope.gin = property.Value;
}
if(property.Key === "FirstName")
{
$scope.fullName = property.Value;
}
else if (property.Key === "LastName") {
$scope.fullName += " " + property.Value;
}
if(property.Key === "WorkEmail") {
$scope.workEmail = property.Value;
}
console.log(property.Key+":"+property.Value);
});
}, function(error) {
console.log("An error has occurred trying to get employee information." + error);
});
}
$scope.$on('$viewContentLoaded', function(event)
{
_init();
});
}
angular.module('myApp').controller('newPurchasingCardController', ['$scope', '$rootScope', '$filter', '$window', '$location', '$timeout', 'requestService', createnewPurchasingCardController]);
}());
And in the html view I just have
HTML
<input ng-model="gin" type="gin" class="form-control" id="gin" placeholder="GIN">
<input ng-model="fullName" type="name" class="form-control" id="name" placeholder="Name">
<input ng-model="workEmail" type="email" class="form-control" id="email" placeholder="Email">
I have tried with viewContentLoaded but not working, what would be a good approach to accomplish this? Thanks.
You are mixing angularjs with Jquery and that causes all the problems.
Use $http service for get request : https://docs.angularjs.org/api/ng/service/$http#get
var getCurrentUserData = function () {
var queryUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/SP.UserProfiles.PeopleManager/GetMyProperties";
$http({
url: queryUrl,
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
cache: false
})
.then(function(response) {
$scope.gin = response.data.EmployeeNumber;
// here set scope variables directly
})
.catch(function(response) {
console.error('error', response.status, response.data);
})
.finally(function() {
console.log("finally ");
});
}

Angular throwing exception 'module cant be loaded'!

I was trying to clean my angular app code up. So I moved all the controllers in their own files. But when I moved the controllers, my main app stoped working and started throwing the exception below -
Error: $injector:modulerr
Module Error
Then I tried searching for the why the module won't load but with no luck.
main.js /*File where app module is declared*/
var app = angular.module('app', ['ngRoute','thatisuday.dropzone','UserController','LinkController','articleController']);
I tried injecting the dependency for the controller files.
Controllers:
Link Controller
var app = angular.module('app');
app.controller('LinkController', ['$scope','$http','$sce',function ($scope, $http, $sce) {
/*Sce declaration required for proxy settings*/
$scope.renderHtml = function (html_code) {
return $sce.trustAsHtml(html_code);
};
$scope.trustSrc = function (src) {
return $sce.trustAsResourceUrl(src);
};
/*First AJAX request which gets all the links and categories for the user*/
$http({
method: 'GET',
url: '/users'
}).then(function successCallback(response) {
$scope.user = response.data;
}, function errorCallback(response) {
});
$scope.getUser = function () {
$http({
method: 'GET',
url: '/users'
}).then(function successCallback(response) {
$scope.user = response.data;
}, function errorCallback(response) {
});
};
$http({
method: 'GET',
url: '/links'
}).then(function successCallback(response) {
this.orderProp = 'age';
/*the response is saved in scope variables*/
$scope.links = response.data[0];
$scope.categories = response.data[1];
$scope.categorytolink = response.data[2];
}, function errorCallback(response) {
console.log('There was a problem! Refresh!');
});
/*AJAX request for getting the recommendations according to the most viewed stars*/
$http({
method: 'GET',
url: '/recommendations/top'
}).then(function successCallback(response) {
$scope.recommendations = response.data;
}, function errorCallback(response) {
});
/*AJAX request when a user clicks a link retrieves the link data*/
$scope.getLinkData = function (link) {
$http({
method: 'GET',
url: "/proxy",
headers: {
"X-Proxy-To": link.rss_link
}
}).then(function successCallback(response) {
/*AJAX request: add a star to the link*/
$http.post('/links/' + link.id + '/views/add', {'link': link}).then(function successCallback(data, status, headers, config) {
// Manually increment star for link just clicked
var $data;
$data = data.data;
$scope.link = $data;
console.log('200 OK! Star added');
}, function errorCallback() {
console.log('Error!');
});
/*The data will be retrieved and will be sorted according to the requirements of welcome.blade*/
$myXml = response.data;
$xmlObj = $.parseXML($myXml);
$newsItems = [];
$channelImage = $($xmlObj).find("channel>image");
/*the information of the link is sorted */
$linkData = {
"title": $channelImage.find("title").text(),
"link": $channelImage.find("link").text(),
"imgurl": $channelImage.find("url").text()
};
/*the data is sorted below*/
$.each($($xmlObj).find("item"), function (index, value) {
$newsItems.push({
"title": $(value).find("title").text(),
"description": $(value).find("description").text(),
"link": $(value).find("link").text(),
"date_published": moment($(value).find("pubDate").text()).format('MMMM Do YYYY'),
"time_published": moment($(value).find("pubDate").text()).format('h:mm:ss a'),
"guid": $(value).find("guid").text()
})
});
$scope.newsItems = $newsItems;
$scope.linkData = $linkData;
}, function errorCallback(response) {
});
};
/*Create a category private to the user*/
$scope.create_category = function (category) {
/*AJAX request: adds a new category*/
$http.post('/categories/new', {'category': category}).then(function successCallback(response) {
/*AJAX request: Updates the categories for the use of new category*/
$http({
method: 'GET',
url: '/categories'
}).then(function successCallback(response) {
$scope.categories = response.data;
}, function errorCallback(response) {
});
}, function errorCallback(response) {
});
};
}]);
User Controller
var app = angular.module('app');
app.controller("UserController", ['$scope','$http','$sce', function ($scope, $http, $sce) {
/*Sce declaration required for proxy settings*/
$scope.renderHtml = function (html_code) {
return $sce.trustAsHtml(html_code);
};
$scope.trustSrc = function (src) {
return $sce.trustAsResourceUrl(src);
};
$scope.dzOptions = {
paramName: "file",
dictDefaultMessage: "<h4><i class='fa fa-camera'></i> <b>Upload</b></h4>",
createImageThumbnails: false,
autoDiscover: false
};
$scope.dzCallbacks = {
'sending': function (file, xhr, formData) {
formData.append('_token', $('#csrf-token').val());
},
'success': function (file, response) {
$scope.user = response;
$.notify("Profile photo changed!", "success", {autoHide: true, autoHideDelay: 500});
}
};
/*Update user info*/
$scope.updateUser = function () {
/*AJAX request: update user info*/
$http.post('/users/update', {
'name': $scope.user.name,
'username': $scope.user.username,
'email': $scope.user.email
}).then(
function successCallback(data) {
$scope.user = data;
$.notify("User updated!", "success", {autoHide: true, autoHideDelay: 500});
console.log('200 OK! User updated');
}, function errorCallback() {
console.log('Error!');
});
};
}]);
Article Controller
var app = angular.module('app');
app.controller("articleController", ['$scope','$http','$sce', function ($scope, $http, $sce) {
/*Sce declaration required for proxy settings*/
$scope.renderHtml = function (html_code) {
return $sce.trustAsHtml(html_code);
};
$scope.trustSrc = function (src) {
return $sce.trustAsResourceUrl(src);
};
/*Populates the comments for particular
* */
$scope.populatecomments = function (newsItem) {
$http({
method: 'GET',
url: '/articles/' + newsItem.guid + '/comments'
}).then(function successCallback(response) {
$scope.comments = response.data;
}, function errorCallback(response) {
});
};
$scope.data = [];
$scope.comment = [];
$scope.btn_add = function (newsItem) {
if ($scope.txtcomment != '') {
$scope.data.push({
"comment": $scope.txtcomment,
"guid": newsItem.guid
});
$http.post('/comments/new', {
"comment": $scope.txtcomment,
"guid": newsItem.guid
}).then(function successCallback() {
var encodedURI = encodeURIComponent(newsItem.guid);
$http({
method: 'GET',
url: '/articles/' + encodedURI + '/comments'
}).then(function successCallback(response) {
$scope.comments = response.data;
$scope.txtcomment = "";
}, function errorCallback(response) {
});
}, function errorCallback() {
console.log('Error comment!');
});
}
};
$scope.savearticle = function (newsItem) {
$http.post('/saved-articles/save', newsItem).then(function successCallback(response) {
/*console.log(document.getElementById("save/"+newsItem.guid).className="disabled");*/
}, function errorCallback(response) {
});
}
/**
* The saved articles by the user will be retrieved when a button clicked
*/
$scope.getSavedArticles = function () {
/*AJAX request: retreive the saved the saved articles for the user*/
$http({
method: 'GET',
url: '/saved-articles'
}).then(function successCallback(response) {
$scope.linkData = null;
$scope.newsItems = response.data;
}, function errorCallback(response) {
});
};
}]);
HELP needed!
Yo do not need to declare module in each controller file. Remove the line in each controller
var app = angular.module('app');
You are injecting controller in you module like dependency.
Change your main.js file to this:
var app = angular.module('app', ['ngRoute','thatisuday.dropzone']);
#Sajeetharan is right you do not need module declaration in all controllers.
Since you are using laravel according to your comment. ( It will conflict with your blade template because both use same {{ }} for variables )
There are two ways to do this:
Change the Angular Tags
var app = angular.module('app', [], function($interpolateProvider) {
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
});
Now Laravel will use the {{ variableName }} and Angular will use <%
variableName %>.
Change the Laravel Blade Tags
Blade::setContentTags('<%', '%>');// for variables and all things Blade
Blade::setEscapedContentTags('<%%', '%%>');// for escaped data
Variables will be: <% $variable %>. Comments will be: <%-- $variable
--%>. Escaped data will look like: <%% $variable %%>.
You can check this Tutorial for more info.

Countdown in Angular, with restart

I have created a simple dashboard with some data as a tryout in Angular. With PHP I get some weather data, news via Google News and 10 tweets about a keyword.
With the $interval I refresh the dashboard every 10 seconds, but I want a countdown from 10 to 0, which starts over and over when the interval is triggered.
Can somebody help me to achieve this?
Current code as submitbutton and $interval trigger:
$scope.formSubmit = function(){
$scope.getResults();
if($scope.interval){
intervalController();
}
}
function intervalController(){
$interval($scope.getResults, 10000);
}
$scope.getResults = function(){
if($scope.city){
$http({
method: 'POST',
url: 'server.php',
data: {city : $scope.city}
}).then(function successCallback(response){
console.log(response.data);
//some data processing here
}, function errorCallback(response){
})
}
}
$scope.initialCountDownValue = 10;
$scope.countDownValue = $scope.initialCountDownValue;
var intervalCanceller = null;
$scope.formSubmit = function(){
$scope.getResults();
if($scope.interval){
intervalController();
}
}
function decrementCountdown() {
$scope.countDownValue -= 1;
if ( $scope.countDownValue === 0) {
$scope.getResults();
$scope.countDownValue = $scope.initialCountDownValue;
}
}
function intervalController(){
intervalCanceller = $interval(decrementCountdown, 1000);
}
$scope.getResults = function(){
if($scope.city){
$http({
method: 'POST',
url: 'server.php',
data: {city : $scope.city}
}).then(function successCallback(response){
console.log(response.data);
//some data processing here
}, function errorCallback(response){
})
}
}
in $scope.countDownValue you have your countdown value to display to the user.
One additional point.
Do not to forget to unsubscribe your $interval on the scope destruction. Or you will have an interval living for ever for nothing.
Here is the way to destroy your interval properly :
$scope.$on('$destroy', function() {
if (intervalCanceller) {
$interval.cancel(intervalCanceller);
}
});

How to implement angularjs async?

I am using AngularJS v1.5.8, My requirement is when i click the Next button it'll display 'Processing...' inside button as text before complete the operation, i have included the $q with my services to get the asynchronous facility, but not working. please see my below codes.
Service
mainApp.factory('PINVerificationServices', ['$http', '$rootScope','$q', function ($http, $rootScope) {
return {
IsPermitted: function (param) {
return $q($http({
url: '/Api/ApiPINVerification/IsPermitted/' + param,
method: 'POST',
async: true
}));
}
};
}]);
Controller
mainApp.controller('PINVerificationController', function ($scope, $rootScope, $state, $window,$q, PINVerificationServices) {
$scope.SubmitText = "Next";
$scope.Next = function () {
$scope.SubmitText = "Processing...";
PINVerificationServices.IsPermitted($scope.PIN).then(function (result) {
$scope.SubmitText = "Next";
});
}
}
HTML
<div class="list-group list-group-sm">
<div class="list-group-item">
<input class="form-control" ng-model="PIN" placeholder="PIN" required id="PIN" name="PIN" type="text" />
</div>
</div>
<button type="submit" ng-click="Next()">{{SubmitText}}</button>
Try this:
return $http({
method: 'POST',
url: '/Api/ApiPINVerification/IsPermitted/' + param
});
Make below changes (from your requirement of nested $http).
In factory Use only $http, and no need of $rootScope as well, It should be like :
mainApp.factory('PINVerificationServices', ['$http', function ($http) {
return {
IsPermitted: function (param) {
return $http({
url: '/Api/ApiPINVerification/IsPermitted/' + param,
method: 'POST'
});
},
GetStudentInformationByPIN : function () {
return $http({
url: '/Api/ApiPINVerification/GetStudentInformationByPIN /',//your api url
method: 'GET'
});
}
};
}]);
In controller make use of $q.all() :
mainApp.controller('PINVerificationController', function ($scope, $rootScope, $state, $window,$q, PINVerificationServices) {
$scope.SubmitText = "Next";
$scope.Next = function () {
$scope.SubmitText = "Processing...";
$q.all([PINVerificationServices.IsPermitted($scope.PIN),
PINVerificationServices.GetStudentInformationByPIN($scope.PI‌N),
//other promises
]).then(function (result) {
if(result[0].data){
$scope.SubmitText = "Next";
}
if(result[1].data){
// studentdata response handling
}
});
}
}

Angular Js $scope.$apply doesn't work

I have a form through which i am adding the data. Same is the case for data update. I populate form by getting data against a particular id via $http post. I have changed the scope variables but not getting modified values. Here are my efforts :
<input type="hidden" name="form_type" ng-model="formData.formType" ng-init="formData.formType = submit_button" value="{{submit_button}}"/>
<input type="hidden" name="student_id" ng-model="formData.studentId" ng-init="formData.studentId = student_id" value="{{student_id}}"/>
$scope.Edit = function (id) {
$http({
method: 'POST',
url: 'process.php',
data: $.param({action: 'fetch', id: id}),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function (response) {
if (!response.success) {
alert("something went wronge");
return false;
} else {
$scope.formData = response.data;
$scope.student_id = response.data.id;
$scope.submit_button = 'Update';
$scope.$apply();
}
});
};
This is the way of the safe apply in the scope.
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
$scope.safeApply(function() {
alert('Now I'm wrapped for protection!');
});

Categories

Resources