AngularJs view not updating when parent property is changed from child controller - angularjs

I'm trying to play around with extending controllers so the same code can be used in multiple places but extended with additional functionality where required. eg. when some functionality is available to some users but not others.
So I'm starting with a little experimentation with a very basic example. I have a simple controller and a more advanced controller that I extend with the simple controller. It all works so far, except when a function in the simple controller changes a property, the view doesn't update, although the property does update.
var app = angular.module("myApp", []);
app.controller("myCtrl", ['$scope', '$http', '$anchorScroll',
function($scope, $http, $anchorScroll) {
var vm=this;
vm.base_property='this is the base';
vm.baseFunction = function()
{
console.log('base function pressed');
vm.base_property='base function pressed';
console.log(vm.base_property);
}
}]);
app.controller("myCtrl2", ['$scope', '$http', '$anchorScroll', '$controller',
function($scope, $http, $anchorScroll, $controller) {
var vm=this;
angular.extend(vm, $controller("myCtrl", {
$scope: $scope
}));
vm.extended_property='this is extended';
vm.extendedFunction =function()
{
vm.extended_property = 'extended pressed';
}
}]);
<div id="app" ng-app="myApp" ng-controller="myCtrl2 as cal">
base Property: {{cal.base_property}}<br />
<button ng-click="cal.baseFunction()" >Base Function</button><br />
extended Property: {{cal.extended_property}}<br />
<button ng-click="cal.extendedFunction()" >Extended Function</button>
</div>
So clicking the extended function button , the text in the view for cal.extended_property updates, but clicking the base function button, the console output indicates the function is run and that the base_property has been updated but the value in the view doesn't change
I tried added $scope.$apply() but that just throws an error Error: [$rootScope:inprog] http://errors.angularjs.org/1.6.4/$rootScope/inprog?p0=%24apply (not ideal anyway as the real script is a lot more complex and I didn't want to have to be running that every time the base controller changes a property)
If there's a better way to achieve what I'm trying to do that isn't too complicated, I'm open to that too. Essentially I have a controller and I want to, in some circumstances, add additional properties and methods to that controller and ideally have it function seemlessly as if it were all the 1 controller

Related

Using textangular functions in angular controller?

I am new to angular and am trying to alter the default behavior of a textangular text input field on my site's backend (I am a guitarist, not a web developer, but can usually figure out what I need to do...). The field has an option to specify a youtube url and then textangular converts that to an image for purposes of editing in the backend. Then in front end textangular has means to have a youtube url displayed correctly as an iframe.
However, on the front end I am not using angular and I read that in my case, to get a youtube embed to show up in front end as iframe you have to use the taApplyCustomRenderers function.
For example, see here:
how to strip placeholder img in textAngular editor in scope var?
In my angular app, here are some of the relevant lines:
angular.module('dashboard')
.controller('EditController', ['$scope', '$http', '$location', '$routeParams', function ($scope, $http, $location, $routeParams) {
$scope.save = function () {
$scope.busy = true;
// I thoguht I could do this:
$scope.somefield = taApplyCustomRenderers($scope.somefield);
// then I would save to model
});
I am getting error that taApplyCustomRenderers is undefined. I thought based on what I read that taApplyCustomRenderers is an available function when using textangular but being new to this I suppose I am missing some key step about injecting the function into the controller or something.
Hoping someone can shed some light.
Thanks in advance!
Brian
TLDR; When you try to access taApplyCustomRenderers you are recieving an error since it is not given to this current function, Inject the function and it will work.
The Problem
While I have never actually tried using textAngular, let me explain what the problem is, and from there it should be easy to find the solution.
Your EditController is just a regular javascript function that gets run and attached to the relevant DOM element, so it only has access to functions that are declared in its own scope (or globally).
Here is your exact code just indented differently so you can understand better:
angular.module('dashboard').controller(
'EditController',
[
'$scope',
'$http',
'$location',
'$routeParams',
function ($scope, $http, $location, $routeParams) {
...
$scope.somefield = taApplyCustomRenderers($scope.somefield);
}
]
);
As you can see, the controller function has two parameters, the first being a string and the second an array, and the last element in the array is just a regular function.
The solution
Checking the textAngular documentation I saw that the taApplyCustomRenderers is a factory, which means you can inject it into your controller function as so:
angular.module('dashboard').controller('EditController',
['$scope', '$http', '$location', '$routeParams', 'taApplyCustomRenderers',
function ($scope, $http, $location, $routeParams, taApplyCustomRenderers) {
taApplyCustomRenderers(); // is now Available.
}
]);

Angularjs - Refresh Control

I am a novice, and don't fully understand functions and how/where they are called but I want to refresh a control that has buttons on it that can be enabled/disabled based on state of something else.
The state is determined when the html for that ctrl is loading.
Question is:
For this control, will the reload() command reload only the TaskCtrl, or the entire web page containing all controls? I don't want to reload the entire page.
var mainCtrl = angular.module('MyCtrl', []);
mainCtrl.controller('TaskCtrl', ['$scope', '$routeParams', '$window', '$location', '$rootScope', '$log',
function($scope, $routeParams,
$window, $location, $rootScope, $log)
{
$scope.loadStuff = function() {
.......
$scope.studyToken = ret.studyToken;
$scope.studies = ret.studies;
$window.location.reload();
}
I notice that "loadStuff" function is in another control so I'm assuming that this is a "scope" function that when it executes it can be local in other controls and perform other functions in that control when it executes?
Any suggestions on references for learning these concepts are welcome.

How to execute code once in the beginning of an Angular app

I have a situation where I need to have a piece of code execute only once at the start of the angular app. My app searches for a beacon using the cordova-plugin and if it finds one, the app is redirected to another state otherwise it stays on the home/splash state.
Currently I have this code running in the "home" controller and it is working but I was hoping for a more elegant solution. I am controlling the state change using a simple if/else statement and a variable on the $scope.
Here's my controller:
homeCtrl.$inject = ['$scope', '$state', '$rootScope', '$ionicPlatform', '$cordovaBeacon'];
function homeCtrl($scope, $state, $rootScope, $ionicPlatform, $cordovaBeacon) {
$scope.skip = false;
$ionicPlatform.ready(function() {
$cordovaBeacon.requestWhenInUseAuthorization();
$rootScope.$on("$cordovaBeacon:didRangeBeaconsInRegion", function(event, pluginResult) {
if (!$scope.skip) {
$scope.skip = true;
$state.go('app.beacon');
}
});
$cordovaBeacon.startRangingBeaconsInRegion($cordovaBeacon.createBeaconRegion("abcdefg", "8484848484848484848"));
});
}
Thanks in advance for help.
Use Run
app.run(function('$scope', '$state', '$rootScope', '$ionicPlatform', '$cordovaBeacon'){
function homeCtrl($scope, $state, $rootScope, $ionicPlatform, $cordovaBeacon) {
$scope.skip = false;
$ionicPlatform.ready(function() {
$cordovaBeacon.requestWhenInUseAuthorization();
$rootScope.$on("$cordovaBeacon:didRangeBeaconsInRegion", function(event, pluginResult) {
if (!$scope.skip) {
$scope.skip = true;
$state.go('app.beacon');
}
});
$cordovaBeacon.startRangingBeaconsInRegion($cordovaBeacon.createBeaconRegion("abcdefg", "8484848484848484848"));
});
Run will be executed first before any controller is loaded or any service is injected.
ngInit is something that you need in your case
Whereever you're defining your app or controller you can point the method that you want to execute at the beginning of your app only once in ng-init attribute.
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="" ng-init="myText='Hello World!'">
<h1>{{myText}}</h1>
<p>The ng-init directive has created an AngularJS variable, which you can use in the application.</p>
</div>
</body>
</html>
source
However, angular docs strictly states that you should not use ngInit for initializing purposes. (see docs)
This directive can be abused to add unnecessary amounts of logic into
your templates. There are only a few appropriate uses of ngInit, such
as for aliasing special properties of ngRepeat, as seen in the demo
below; and for injecting data via server side scripting. Besides these
few cases, you should use controllers rather than ngInit to initialize
values on a scope.
Initializing values is something that should be done from controller. (see docs)
Use controllers to:
Set up the initial state of the $scope object. Add behavior to the
$scope object.

Access Angular $scope in plain javascript

I have a Controller which has something like this...
angular.module('kZoneApp').controller('DemandController', ['$http', '$scope', '$rootScope', 'dataFactory', '$window', '$routeParams','$sce', function ($http, $scope,$rootScope, dataFactory, $window, $routeParams,$sce) {
$scope.channel = $routeParams.channel;
now within my page.html, I want to do the following....
<script>
YoutubeVideoPlayer.openVideo('{{channel}}');
</script>
please note, I am using Routes, and page.html is loaded into my ng-view Via a Route.
<div id="myView" class="reveal-animation" ng-view></div>
I tried below code, but it returns undefined....
$().ready(function () {
var channel = angular.element("#myView").scope().channel
YoutubeVideoPlayer.openVideo(channel);
})
How can I get the value of Channel within my Template View?
Update
below works...but I am sure there has to be a cleaner solution
setTimeout(function () {
alert(angular.element("#myView").scope().channel)
}, 500);
A workaround solution should be creating a variable before your Angular.module definition.
var scopeInAngular = null;
and inside controller assign controller to your variable.
scopeInAngular = $scope;
then if you manipulate anything inside angular view use
scopeInAngular.$apply();

$window on ngClick directive

Why this doesn't work ?
Since angular expression doesn't have access to window object, i've used $window, however the below doesn't work.
<button ng-click="$window.alert('Hi There')">Hi There</button>
Angular expressions do not have access to global variables like
window, document or location. This restriction is intentional. It
prevents accidental access to the global state – a common source of
subtle bugs.
A template only has access to variables that are put on its $scope. If you need to access anything on $window from your template you'll need to inject $window into your controller and assign it to $scope there.
For example
angular.module('app').controller('Controller',
['$scope', '$window', function($scope, $window) {
$scope.$window = $window;
}]);
As pointed out in the comments, you probably don't want to expose the entire $window wrapper to your template so a better approach is to use a helper function on $scope.
ng-click="greet('Hi There')"
angular.module('app').controller('Controller',
['$scope', '$window', function($scope, $window) {
$scope.greet = function(message) {
$window.alert(message);
};
}]);
You can only call services in your controllers through scope (this is the idea of separating non-UI logic from the template)
See How to call a service function in AngularJS ng-click (or ng-change, ...)?
$window is a service, and like other services that don't relate directly to the view, they are not accessible in the templates.
angular.module('app').controller('Controller',
['$scope', '$window', function($scope, $window) {
$scope.alert=$window.alert
}]);

Resources