Dynamic Application Use Case
The application I am currently developing is highly dynamic in nature. It adapts to changing inputs based on a template variable, and uses directives to fetch the required templates and construct HTML output accordingly. Each template has associated events, which require the destruction of childScopes to trigger appropriate destroy events when the scope changes. Failure to call the destroy method results in a memory leak.
[edited] To elaborate further, at the core we have a generic widget that displays a button whose value changes with each click, or for visually impaired users, renders a list of radio buttons instead. These buttons act as "hotspots" over an image to evaluate joint pain scores. While visually impaired patients cannot see the buttons, their screen readers can read out the radio inputs. A tablet is used for data capture, passing between researcher, patient, and doctor. There's a toggle button to switch between visual and visually impaired modes for quick comprehension by doctors. Due to spotty hospital WiFi, I utilized Angular to create a single-page application for this purpose.
The process involves passing a template variable into the directive. The link then reads this variable and loads the corresponding cached templates. Upon pressing the button, the template may change, prompting the link to re-read it. However, I encountered a memory leak issue when switching from a slider to a radio list input for visually impaired users using a jQuery slider. The more switches between views, the more memory consumed. But calling the destroy method before $compile resolves the memory leak problem. To avoid destroying everything with scope.$destroy, I implemented a childScope.
This task could be achieved using ng-if or ng-switch based on the template variable, but we chose a programmatic approach since future requirements involve dynamic changes in multiple views such as "doctor view," "researcher view," "patient view," "patient visually impaired view," "elderly view," etc., each with unique appearances and functionalities tailored to specific needs.
Challenge
I'm facing difficulty binding the model to the generated childScope due to $new() creating an isolated scope that struggles to communicate externally. My primary goal is to properly destroy the scope to prevent memory leaks.
My queries are:
- Is there a way for the childScope to bind with the parent ng-model?
- Is there an alternative method to trigger destroy?
Code Example
Here is a simplified version of my code focusing on essential components.
Template
<div ng-app="app" ng-controller="ctrl">
<div>
<h1>Input</h1>
<doodad ng-model="foo"></doodad>
<nested-doodad ng-model="bar"></nested-doodad>
<nested-doodad ng-model="qux.value"></nested-doodad>
</div>
<div>
<h1>Output</h1>
<span>{{ foo }}</span>
<span>{{ bar }}</span>
<span>{{ qux | json }}</span>
</div>
</div>
Application
function ctrl($scope) {
$scope.foo = "foo";
$scope.bar = "bar";
$scope.qux = { value : "qux" };
}
angular
.module('app', [])
.directive('doodad', function($compile) {
var linker = function(scope, element) {
var childScope;
/* Method to trigger destroy on child widgets */
var getNewScope = function(oldScope) {
if (oldScope) {
oldScope.$destroy();
oldScope = null;
element.html('');
}
return scope.$new();
};
var renderTemplate = function() {
childScope = getNewScope(childScope);
/* Template is dynamic - hardcoded for example */
/* Events & other bindings occur here */
element.html('<input type="text" ng-model="ngModel" />');
$compile(element.contents())(childScope);
}
scope.$watch('template', renderTemplate);
}
return {
restrict: 'E',
replace: true,
require: '^ngModel',
scope: {
ngModel : '=',
template: '='
},
link: linker
}
})
.directive('nestedDoodad', function() {
return {
restrict: 'E',
replace: true,
scope: {
ngModel : "="
},
template: '<div><doodad ng-model="ngModel"></doodad></div>'
}
});