Keep checkbox checked after navigating away from page - javascript

I have a check box that I need to keep checked after navigating away from the page. I am using AngularJS and bootstrap. Right now I am resetting the bound variable to false every time I reload the page (when the controller runs): how can I store my most up to date variable ($scope.disableCheck)?
In my controller....
$scope.disableCheck = false;
$scope.removeCheck = function () {
$scope.disableCheck = !$scope.disableCheck;
}
And in my HTML...
<input class="notification-checkbox" type="checkbox" value="{{disableCheck}}" ng-click="removeCheck()" ng-clicked="{{disableCheck}}">

Try using $rootScope instead. It is global to all controllers
Something like this
.controller('someCtrl', function($scope, $rootScope) {
$rootScope.disableCheck = true;//set root scope here and refer to it as needed from other controlers
})

Related

$scope value not updating in view when $scope value is changed in controller

I have a function which uses papaparse.js to immediately get the content of a .csv file selected via a file input as a JSON object list, I then pass this list to a $scope which I console.log.
This is triggered on a button click and works, the issue is the view can't immediately see this $scope has been updated until another action happens, such as clicking the button again, or clicking another button which calls an empty $scope function.
HTML View:
<div ng-controller="myController">
<input id="csvfile" type="file" accept=".csv">
<button type="button" ng-click="readDataList()">
Preview Data
</button>
{{dataList}}
</div>
And the .js is
var App = angular.module('App', ["ui.bootstrap"]);
App.controller('myController', function ($scope, $http) {
$scope.dataList;
$scope.readDataList = function ()
{
var myfile = $("#csvfile")[0].files[0];
var json = Papa.parse(myfile,
{
header: true,
skipEmptyLines: true,
complete: function (results)
{
$scope.dataList = results.data;
console.log($scope.dataList);
}
});
};
});
The list is in the console as soon as the button is clicked, but won't appear in the view unless I clikc the button again.
If I add into the contoller the following:
$scope.testScope = 'hello';
$scope.changeScope = function () {
$scope.testScope = 'goodbye';
}
and in the view have the new $scope displayed and the function on a new ng-click this works immediately displaying the new value as soon as the button is clicked. But it doesn't work for the data from the csv even though it appears in console.
Try $scope.$apply(); after the line $scope.dataList = results.data;
Your Papa.parse function is out of the scope of angular, due to which any changes done in its callback are not captured by angular. So you have to manually trigger the new digest cycle with $scope.$apply() function so that angular checks if anything has changed in its scope and updates the view.
Read this for more detailed information http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
I could able to resolve the issue by using $rootScope, I have used $rootScope and the value is automatically updating the view (HTML template).
Controller:
$rootScope.myVariable = 'update label'
Now, if you update the value of myVariable via any click/change events the updated value will be updated in the HTML template.
HTML file:
<h3>{{myVariable}}</h3>

Best way to toggle Booleans between $rootScope and and a controller

I have a navbar directive which sits above ng-view. It utilises the $rootScope to trigger events to show buttons in certain views.
I am trying to add a button to the directive template which will switch a boolean in a controller for a particular view. The view shows a period of time and each period has a particular boolean that I want to switch from the directive.
The boolean value is saved in a local storage object which is initialized when each iteration of this particular view is loaded.
First, the value needs to be communicated to the directive so the button can display as being set to true or false. When the switch is toggled, the value of that boolean needs to make its way from the directive, through the $rootScope, to the controller and then be saved in the storage object.
When the view is changed, the whole process needs to repeat. The switch needs to be able to be switched on and off multiple times, obviously.
At present, I am emitting the value from the controller to the $rootScope and then listening for that value in the directive link function.
However, what is the best way to get that $rootScope value BACK into the controller. I tried setting up a $rootScope.$watch in the controller which appeared to work on any single page but when navigating between different time periods, the $rootScope value of the boolean was not resetting properly.
I tried resetting the value in the controller initialization as follows:
$rootScope.booleanValue = false;
but this didn't work.
I have also tried the following:
$scope.$on('$routeChangeSuccess', function (next, current) {
$rootScope.booleanValue = false;
});
but I can't get the whole cycle to work properly. It still seems as though the value of the property in the $rootScope is not resetting from the view before and is then carrying over when an adjacent pay period view is loaded.
I hope this makes sense. I will save you from too much code as I think the basic idea is here.
What you are trying to do is share state from your navbar directive (an isolate scope) and your view's controller. I recommend you use a factory provider service to share that state:
angular.module('myApp').factory('navbarState', function (){
return {started: false}
});
In your navbar directive, inject the service and store the state in that service:
angular.module('myApp').directive('navigationBar', [
'$rootScope',
'navbarState',
//'NavigationStackService',
//'NavigationBarService',
function ($rootScope, navbarState) {
function link(scope, element) {
scope.startEditMode = function(){
console.log("Edit clicked");
navbarState.started=true;
//NavigationBarService.hideNavigationEdit();
//NavigationBarService.showNavigationDone();
};
scope.finishEditMode = function(){
console.log("Done clicked");
navbarState.started=false;
//NavigationBarService.hideNavigationDone();
//NavigationBarService.showNavigationEdit();
};
}
return {
templateUrl: 'templates/navigation-bar.html',
restrict: 'E',
scope: {},
link: link
};
}
]);
In your view controller, retrieve the service, put it on the controller's scope, and use it in your template.
angular.module('myApp').controller('controller2', function(navbarState) {
console.log("view controller2 started");
var vm = this;
vm.navState = navbarState;
vm.message = "hello from ct2";
});
The DEMO on JSFiddle.

Why variable in $scope does not update?

Code in plunker.
I have two controllers. In first controller i have handler for button in my view
$scope.click = function () {
$location.url("/some");
console.log("clicked");
}
In handler i change URL. I also configured my $routeProvider.
var app = angular.module('plunker', []).config( function ($routeProvider) {
$routeProvider.when('/some', {template: " counter is {{n}}</br> <button ng-click='click()'>click to load new url but still with \"loading cntl\"</button>", controller: "loading"})
.when("/second",{controller: "loading"});
});
Here i have two different routes that have the same controller - loading controller
So after my URL was changed to /some new button appears in my view. I have another handler for this button in loading controller.
app.controller("loading", function ($scope,$location) {
$scope.n= $scope.n || 0;
console.log("at the begining n is "+ $scope.n);
$scope.click = function click() {
$scope.n++;
console.log("n after++ is " + $scope.n);
$location.url("/second");
}
});
Here i increment n variable and change URL to /second. In my $routeProvider i indicated that route with this URL must have loading controller as well. After triggering of the button it disappears because /second router have no template. I press button on main view again, my loading controller is executed once more, but the n variable is still 0. Why the value of n is not 1?
I know that my explanation is confusing, but i have the code in plunker
You're instantiating a new controller that has a new scope (and thus a new variable n). You need to keep the iteration data in something more persistant, like a parent scope, service, localStorage, etc (depends what you need the data for).
Here's a working example using $rootScope, just so you can see (you should probably use something other than the $rootScope in your final code): http://plnkr.co/edit/HETROBPwa6VyjX83Eev0?p=preview
I added a simple console.log('scope', $scope); in the controller, so you can see that each time you change the url, a new scope is created.

Share selected select with another controller

I am trying to to share a selected option with another controller (And have it update when I select a new option). Something that would work like the 2-way data binding between controllers.
I've attempted this by setting up a factory like so
.factory("shareObjective", function($scope){
var shareObjective = {};
return {
shareObjective: shareObjective,
};
})
Then I inject this into the controller and bind it to the model of the select like so
$scope.selectModel = shareObjective.shareObjective;
I seem to be having some trouble getting this to work. I Basically just want to share the selected option (it's .name to be precise) with another controller and am struggling to do so. My first step was to get it to share into the factory to begin with, but I seem to be having no luck attempting this. Should I be using something like the $broadcast to keep the stream of information open? Thanks!
Edit - here's a plunkr http://plnkr.co/edit/L5lz4etQ7mUEhf9viNOk?p=preview
Yes, this won't work by default because you use two different scopes thus different ngModels.
Using a service also won't help because even if a click on a select with a ngModel in one scope will trigger a digest loop in that scope, it won't trigger it in the other scope.
As you suggest yourself you need to somehow notify the other scope to update itself, and yes you can do this through events ($broadcast and $on):
Both controllers contain
<select
ng-model="foo"
ng-options="foo for foo in foos"
></select>
And this is the JS:
var foos = [
'foo1',
'foo2',
'foo3'
];
function MyCtrl($scope, $rootScope) {
$scope.foo = foos[0];
$scope.foos = foos;
// detect broadcasts from other controllers and set our value to the one we received
$rootScope.$on('foo-event', function(e, data) {
$scope.foo = data;
});
// detect when foo changes and broadcast it's value
$scope.$watch('foo', function() {
$rootScope.$broadcast('foo-event', $scope.foo);
});
}
myApp.controller('MyCtrl1', ['$scope', '$rootScope', MyCtrl]);
myApp.controller('MyCtrl2', ['$scope', '$rootScope', MyCtrl]);
Here's the fiddle.
Notice that my code does not use a single controller, just a single controller implementation. You can write your own MyCtrl1 and Myctrl2 implementations that both have this code inside.
Also, while it would look like this generates an infinite loop between $watch and $on, it does not. $scope.foo = data; does not trigger $watch,

Reload include on state change in AngularJS

I want to make an include refresh itself when the state changes in my app.
Example:
<div ng-include="'partials/search-form.html'"></div>
Global listener:
phonecatApp.run(function ($state,$rootScope, $log) {
$rootScope.$state = $state;
$rootScope.$on('$stateChangeSuccess', function(){
if( !$state.includes('search') ) {
// refresh the search-form include in the header
}
});
});
How can I do this? Because as the form is outside of the ui-view and when I change the page it still has a value from the previous search query as it's still in the previous state... How can I refresh this so it's updated???
Note: creating another ui-view isn't a solution as this form is just a simple include shown in a header across all pages and doesn't relate to any states, etc.
Update: So you can see what I am doing with the form and controller:
phonecatControllers.controller('SearchCtrl', function($rootScope, $scope, $state, $location) {
$scope.query = ($state.includes('search') ? $location.search()['q'] : '');
$scope.filterQuery = ($state.includes('search') ? $location.search()['q'] : '');
if(!$scope.query){
$location.search('q', null);
}
$scope.queryChanged = function () {
if($scope.query){
$state.go('search', {'q': $scope.query} );
} else {
$location.search('q', null);
}
$scope.filterQuery = $scope.query;
}
});
<form class="form-search" ng-controller="SearchCtrl" ng-submit="queryChanged()">
<input name="q" ng-model="query" id="filter" type="text">
<button type="submit" class="btn">Search</button>
</form>
Update: The best idea I could come up with, would be to empty the input field like so:
var q = document.getElementsByName('q');
q.setAttribute('value', '');
When the state changes is NOT the search state. This automatically fires a change event on the input and then causes it to have it's ng-dirty class removed, etc.
Since the form is available globally, my advice is that you put the query in the $rootScope. The rootscope is available on the entire application (if you inject it, of course), so you would be able to reset the value at any moment.
An even better solution would be to store the query value in the rootscope and access it using a service.
For reloading the route you can use:
$route.reload();
But i'm not sure if that will reload the included file.
Perhaps you can set the string of the included file as a $scope variable and then set it as an empty string first and then put it back again? Not sure if it will work, probably needs a $timeout, $digest or $watch to make sure it is ready to flip.
Another option would be to set an
ng-if="someForm"
var to the first route and when you are done, set it to false and have another
ng-if="someOtherForm"
for the second include. Or use the same but set it to something falsy first.
But if it is about cleaning the form, there are other ways to do that. Like setting all model-vars to an empty string or null. So if you have it at $scope.searchEntry, you set it undefined or "".
In your example:
$scope.query = "";
This is what I have at the moment:
phonecatApp.run(function ($state, $rootScope, $log, $location) {
$rootScope.$state = $state;
$rootScope.$on('$stateChangeSuccess', function(){
if( !$state.includes('search') ) {
$('[name=q]').val('');
} else {
$('[name=q]').val($location.search()['q']);
}
});
});
I've had to use jQuery because Vanialla JS couldn't see the element. But it basically just sets the input value depending on where you are in the app.
The ONLY issue is that if you submit the form when it's empty it will then default back to what it was before! Presumably because the model hasn't been reset!
So if I do this instead:
$('[name=q]').val('').change();
It then triggers the model to be reset (from what I can tell) as it no longer submits the previous result... Thoughts? It feels dirty, but it works!

Categories

Resources