Why does another variable change in AngularJS controller? - javascript

I have a AngularJS controller like below:
schedule.controller('schedule', ['$scope', '$http', function($scope, $http){
$http.get('/zt-api/business/admin/' + window.location.pathname.split('/')[2]).success(function(data){
$scope.admin_times = data;
$scope.admin_times_unix = data;
$scope.weekDays = {'Saturday': "شنبه", 'Sunday': "یکشنبه", 'Monday': "دوشنبه", 'Tuesday': "سه شنبه",
'Wednesday': "چهارشنبه", 'Thursday': "پنجشنبه", 'Friday': "جمعه"};
angular.forEach($scope.admin_times, function (value, key) {
angular.forEach(value, function (value2, key2) {
angular.forEach(value2, function (value3, key3) {
angular.forEach(value3, function (value4, key4) {
angular.forEach(value4, function (value5, key5) {
var info = $scope.admin_times[key]["week_"+ key][key3].times[key5];
if (!isNaN(info)){
var myObj = $.parseJSON('{"date_created":"'+ $scope.admin_times[key]["week_"+ key][key3].times[key5] +'"}'),
myDate = new Date(1000*myObj.date_created);
$scope.admin_times[key]["week_"+ key][key3].times[key5] = myDate.toLocaleString().split(", ")[1]
}
});
});
});
});
});
});
}]);
I change $scope.admin_times, but I don't know why $scope.admin_times_unix also changes!
Can you help me?

Because you are assigning same reference of data to admin_times as well as admin_times_unix object.
That's why changing in one object is, affecting other object.
You could solve this issue by using angular.copy which does create a new reference rather than assigning its actual reference.
$scope.admin_times_unix = angular.copy(data);

Related

How to access Array value from outside scope with Angular JS

Okay so I know this is a really stupid question. I looked everywhere before making this thread and I am just completely unable to figure it out. This is the code I had. My issue is that between passing the value from getJSON to the angular controller, my array has lost its values. What is the correct way to do what I'm trying to do here?
function getJSON(json) {
data = [];
$.each(json, function (index, value) {
data.push(value);
});
console.log(data); // Accurately logs array data
return data;
}
function passValue() {
return getJSON();
}
app.controller('ExampleController', function($scope) {
x = passValue()
$scope.title = x[0]; // Throws error
console.log(x); // Only returns an empty array
}
And this I have in a script on my html file. It works 100%.
$(document).ready(function() {
$.getJSON("{{ url_for('static', filename='movies.json') }}?id={{ movie_id }}", function(json) {
getJSON(json);
});
For example, this works.
function getJSON(json) {
data = [];
$.each(json, function (index, value) {
data.push(value);
});
console.log(data) // Acurrately logs array data
document.getElementById('exampleDiv').innerHTML = data[0] // Accurately appends array data (0 being title)
}
I found a solution that fixes my problem. If anyone has a similar issue, I hope this helps you as well.
function getJSON(json) {
data = [];
$.each(json, function (index, value) {
data.push(value);
});
console.log(data)
update(data)
}
function update(data) {
var $scope = angular.element(document.querySelector('#movie')).scope()
$scope.$apply(function(){
$scope.title = data[0];
});
}
app.controller('MovieController', function($scope) {
});
You can use the window to store and access data between angular and any component. Angular has a wrapper called $window to access it too.
In javascript:
function foo(value){
window.data = value;
}
In angular:
app.controller('ctrl', ['$scope', '$window', function($scope, $window) {
$scope.getData= function() {
//using $window
alert($window.data);
//Or
alert(window.data);
};
}]);

AngularJS Object not updating

I have an object like this:
.controller('lineCtrl', function($sessionStorage, $rootScope, $scope, $stateParams, $state, $filter, config, $http, SCAN_EVENT, $ionicLoading, $ionicPopup, ScanService){
var areaName;
var lineCounts;
function filterObjects () {
switch ($stateParams.area) {
case 'PW':
$scope.items = $filter('filter')($rootScope.runnerItemsPW, {line: $stateParams.line}, true);
areaName = "Putwall";
lineCounts = $rootScope.lineCountPW;
break;
case 'NC':
$scope.items = $filter('filter')($rootScope.runnerItemsNC, {line: $stateParams.line}, true);
areaName = "Non-Con";
lineCounts = $rootScope.lineCountNC;
break;
case 'RE':
$scope.items = $filter('filter')($rootScope.runnerItemsRE, {line: $stateParams.line}, true);
areaName = "Receiving";
lineCounts = $rootScope.lineCountRE;
break;
case 'SP':
$scope.items = $filter('filter')($rootScope.runnerItemsSP, {line: $stateParams.line}, true);
areaName = "Singles Pack";
lineCounts = $rootScope.lineCountSP;
break;
default:
break;
}
}
filterObjects();
$rootScope.lineData = {
position: 1,
lineNumber: $stateParams.line,
area: $stateParams.area,
areaName: areaName,
lineCounts: lineCounts,
items: $scope.items
};
$scope.clearLine = function(){
ScanService.dispatch(String("00" + $rootScope.lineData.lineNumber).slice(-2) + 'DONE');
};
$scope.removeStationItems = function (stationdID, createdDateTime){
var url = config.baseURL + "/runner/removeDesktopRunnerItem/" + stationdID + "/" + createdDateTime.split(' ').join('_') + "/" + $sessionStorage.user.loginName;
$http.get(url).then(function(response){
if(response.status === 200 && response.statusText === "OK"){
var data = response.data;
if(data === ''){
$state.go($rootScope.prevState);
$rootScope.setObjects(data);
filterObjects();
}else{
$rootScope.setObjects(data);
filterObjects();
var found = $filter('getByStationID')(data.runnerItems, $rootScope.lineData.lineNumber);
if(found === null){
$state.go($rootScope.prevState);
}
}
}
})
};
})
.controller('queue', function ($rootScope, $http, $filter, $scope, config, $interval, $ionicLoading) {
$rootScope.setObjects = function (data){
$rootScope.runnerItemsPW = $filter('filter')(data.runnerItems, {areaShort: "PW"});
$rootScope.lineCountPW = $filter('filter')(data.lineCounts, {areaShort: "PW"});
$rootScope.runnerItemsRE = $filter('filter')(data.runnerItems, {areaShort: "RE"});
$rootScope.lineCountRE = $filter('filter')(data.lineCounts, {areaShort: "RE"});
$rootScope.runnerItemsNC = $filter('filter')(data.runnerItems, {areaShort: "NC"});
$rootScope.lineCountNC = $filter('filter')(data.lineCounts, {areaShort: "NC"});
$rootScope.runnerItemsSP = $filter('filter')(data.runnerItems, {areaShort: "SP"});
$rootScope.lineCountSP = $filter('filter')(data.lineCounts, {areaShort: "SP"});
};
});
When the controller is first started the $scope.items is added to the object and works just fine. The line $scope.items is updated when new data comes in. However, the ng-repeat I have for lineData.items does not update.
$filter creates a new object so need to updated $rootScope.lineData. Update the $rootScope.lineData again after filter.
You are breaking the original object reference when you reassign $scope.items so the original reference passed to the lineData object is no longer the same as the array referenced by $scope.items
Simple example
var myArray = [1,2,3];
var obj={
items: myArray // reference to array above
}
console.log(myArray ); // [1,2,3]
console.log(obj.items); // [1,2,3] - exact same object reference as myArray
// now reassign myArray with new object reference
myArray =[6,7,8];
console.log(myArray ); // [6,7,8]
console.log(obj.items); // [1,2,3] - the original reference hasn't changed
// now reassign object property value to new array
obj.items = myArray ;
console.log(obj.items); // [6,7,8] - references new array
NOTE: you should be using a service to share data and methods across controllers.
You are only assigning to $rootScope.lineData once, in the controller initialization function. This function only gets called once, during the bootstrap process.
You need to assign it in your filterObjects function as well so that it gets updated by your $http call. This means that you need to create it before calling filterObjects the first time. Like this:
$rootScope.lineData = {
position: 1,
lineNumber: $stateParams.line,
area: $stateParams.area,
areaName: areaName,
lineCounts: lineCounts,
};
function filterObjects () {
switch ($stateParams.area) {
case 'PW':
$rootScope.lineData.items = $scope.items = $filter('filter')($rootScope.runnerItemsPW, {line: $stateParams.line}, true);
areaName = "Putwall";
lineCounts = $rootScope.lineCountPW;
break;
// etc.
}
}
filterObjects();
In $http response code add line $scope.$apply(); after calling function filterObjects()

Passing an array from service to controller

I cannot seem to figure out how to pass an array from service to a controller.
I have a simple service
.service('test', function() {
var array = []
return array;
})
And a controller where I call this function when a button is pressed
$scope.testArray = function() {
$scope.test = test.array;
console.log("test: ", $scope.test);
};
I get an error test is undefined. Can anyone explain to me please why this doesn't work and how to fix it? I tried storing that array in a separate object but no luck either. THanks
(See also: this SO question about Angular providers)
A service should put properties directly on this. So instead of
.service('test', function() {
var array = [];
return array;
})
try
.service('test', function() {
this.array = [];
})
(code style notwithstanding; many would suggest preferring function access over direct object access)
.service('test', function() {
var array = [];
this.getArray = function(){
return array;
};
})
Just change test.arraywith test:
JSFiddle
.controller('youCtrl', ['$scope', 'test', function ($scope, test) {
$scope.testArray = function() {
$scope.test = test;
console.log("test: ", $scope.test);
};
});
Add the array variable to your service.
angular.module('moduleName').service('test', function() {
this.array = [];
});
Inject your service into your controller.
angular.module('moduleName').controller('controllerName', function(test) {
$scope.test = test.array;
console.log("test: ", $scope.test);
});

How do I refactor a calendarViewModel such that it's not tied to a specific controller?

For a small Angular.js testbed project, I've set up the following plunker:
My Plunked Plunker
Originally, I had a local version of this testbed working, when the calendarViewModel was directly included in the Angular controller.
appControllers.controller('PageController', [
'$scope', '$http', 'Enums', 'ViewModels',
function ($scope, $http, Enums, ViewModels) {
var calendarViewModel = function () {
var pub = {};
pub.date = new Date();
pub.isOpen = false;
pub.today = function () {
if(pub.isOpen)
pub.date = new Date();
};
pub.clear = function () {
if(pub.isOpen)
pub.date = null;
};
pub.hide = function () {
pub.isOpen = false;
};
pub.toggle = function ($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.hideCalendars();
pub.isOpen = !pub.isOpen;
};
return pub;
};
// Backing model for this 'controller'
$scope.viewModel = {
// Properties:
startCalendar: new calendarViewModel(),
endCalendar: new calendarViewModel(),
// data:
// Generates an object that is sent to the server with $http calls.
data: function () {
var object = {
startDate: startCalendar.date.toString(),
endDate: endCalendar.date.toString()
};
return JSON.stringify(object);
}
};
// - Controller-specific functions... ----------------------------------
$scope.hideCalendars = function () {
$scope.viewModel.startCalendar.hide();
$scope.viewModel.endCalendar.hide();
};
$scope.clear = function () {
$scope.viewModel.startCalendar.clear();
$scope.viewModel.endCalendar.clear();
};
$scope.today = function () {
$scope.viewModel.startCalendar.today();
$scope.viewModel.endCalendar.today();
};
// Restricts certain days from being selected.
$scope.disableWeekends = function (date, mode) {
return mode === 'day'
&& (date.getDay() === Enums.DaysOfTheWeek.Sunday
|| date.getDay() === Enums.DaysOfTheWeek.Saturday);
};
// This is a demonstration scope action. Pretty much, the pattern
// I found, is to have a view model expose a method that creates
// a stringified JSON blob that we can send to the server. This
// method is how such a save function would work.
$scope.save = function () {
var promise = $http({
method: 'POST',
url: '/some/server/url',
data: $scope.viewModel.data()
});
promise.success(function (data) {
// Do something with the returned data?
}).error(function (data) {
// Do something with the error data?
});
};
// - End of Controller-specific functions... ---------------------------
// Picker-specific options...
$scope.dateOptions = {
'starting-day': Enums.DaysOfTheWeek.Monday,
'format-day': 'dd',
'format-month': 'MM',
'format-year': 'yyyy',
'min-mode': Enums.PickerMode.Day,
'max-mode': Enums.PickerMode.Year
};
$scope.format = 'MM/dd/yyyy';
$scope.today();
}
]);
Since I refactored it out to the ViewModels constant object, though, I get the following errors from Angular:
TypeError: undefined is not a function
at Object.pub.toggle (http://run.plnkr.co/AKUBdEb5M3KT5DM9/app.services.js:31:4)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:10185:21
at http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:17835:17
at Scope.$eval (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:11936:28)
at Scope.$apply (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:12036:23)
at HTMLInputElement.<anonymous> (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:17834:21)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:2613:10
at forEach (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js:310:20)
The reason I am doing this, is because in theory multiple controllers could have need of calendars that are tied to calendarViewModels (this is why I created the calendarViewModel functional object in the first place.) I want the calendarViewModel construct to not be tied to a specific controller, however, which I have apparently broken by refactoring it in this way.
I think I'm on the right track, but in any event, something is clearly missing. My question: what is the correct way for me to refactor my calendarViewModel, that works and is more easily reusable?
So a few things from your plunker:
Don't use app.constant to do factories. Use app.factory instead, eg:
_
appServices.factory('ViewModels', function() {
var pub = {};
pub.date = new Date();
pub.isOpen = false;
pub.today = function () {
if(pub.isOpen)
pub.date = new Date();
};
pub.clear = function () {
if(pub.isOpen)
pub.date = null;
};
pub.hide = function () {
pub.isOpen = false;
};
pub.toggle = function ($event) {
$event.preventDefault();
$event.stopPropagation();
//hideAll();
pub.isOpen = !pub.isOpen;
};
return pub;
});
Your factories are automatically shared between controllers when you do this:
_
appControllers.controller('FirstController', [ '$scope', 'MyCalendarService', function($scope, MyCalendarService){
$scope.myCalendarService = MyCalendarService;
}]);
appControllers.controller('SecondController', [ '$scope', 'MyCalendarService', function($scope, MyCalendarService){
$scope.myCalendarService = MyCalendarService;
}]);
... if the controllers are defined in parallel in the html. If they are nested, you just need to inject your service at the top level. Ideally, you just need to DI a couple of services in a controller and assign them to the scope.
Does that answer your question?
PS: hideAll is not defined in your plunker, I commented it out and things started to work.
Edit: This edited plnkr should do what you want: http://plnkr.co/edit/7VDYDQhK2CDGnwa8qhWf?p=preview

How to use angularjs factory?

var myappWebApp = angular.module('myappWebApp', ['ui.bootstrap']);
//factory
myappWebApp.factory('wired', function () {
this.currOp = false;
return {
currOp1 : this.currOp
}
});
// controller
myappWebApp.controller('wiredCtrl',
function ($scope, $http, wired) {
//data
$scope.currOp = wired.currOp;//why is this undefined?
$scope.currOpInText = wired.currOpInText();
$scope.altOpInText = null;
$scope.boxA = null;
....
How should my scope.currOp always automatically have the same value as wired.currOp?
If you are copying just a bool, you can't. Booleans are copied by value, so you don't get reference-based updates.
You might consider using an object with a boolean:
myappWebApp.factory('wired', function () {
this.state = { curOp: false };
return {
state: this.state
}
});
Then, when you reference it in your scope, you can do this:
myappWebApp.controller('wiredCtrl',
function ($scope, $http, wired) {
$scope.opState = wired.state;
});
And now when curOp changes, the controller will have the change. You can watch for changes:
$scope.$watch("opState.curOp", function(newVal, oldVal) {
// Handle changes in curOp
});
Or you can bind to it:
CurOp: {{state.currOp}}
Note: You asked: "why is this undefined?" The answer is because your service is exposing currOp1 but you are referencing currOp

Categories