1

I'm using Angular for the UI for an embedded system. I have the app setup so that you can run the UI in a desktop browser (a simulation of the embedded system). I have a service layer that contains all the API functions for the embedded system. So for the simulation, I just swap out the service layer to simulate the API functions. This works well, but now my problem is that I need to actually do stuff in the simulation that is not part of the embedded system.

I do not want any code for the simulation in any of the controllers. I've successfully been able to get a controller for the simulation just by loading a single .js file and even call a function in a separate controller, but I can not change the value of a scope variable. It does not work as I would expect it to.

Here is a demo that demonstrates what happens. I'm calling a function in the first controller from the second controller and passing a parameter. The console prints out the correct value, but the scope variable doesn't change.

var app = angular.module('myApp', [])

.controller('Ctrl_1', function( $scope )
{
  $scope.myFunction = function( myParam )
  {
    console.log('Ctrl_1: ' + myParam );

    // Demonstrates that the function is called, and with the correct value for myParam.
    $('.console').append('<li>' + myParam + '</li>');

    $scope.myVar = myParam;
  }

  $scope.myFunction('Foo');
});

// sim.js
$('.app-wrapper').attr('ng-controller', 'Ctrl_2');

app.controller('Ctrl_2', function( $scope, $controller, $timeout )
{
  var Ctrl_1_ViewModel = $scope.$new();

  $controller('Ctrl_1', { $scope: Ctrl_1_ViewModel } );

  //$timeout( function() { Ctrl_1_ViewModel.myFunction('Bar') } );

  $scope.testFunction = function()
  {
    Ctrl_1_ViewModel.myFunction('Bar');

    console.log('Ctrl_2: ' + Ctrl_1_ViewModel.myVar );
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app="myApp" class="app-wrapper">
  <div ng-controller="Ctrl_1">
    <h3>myVar = '{{ myVar }}'</h3>
    <button ng-click="testFunction()">Set myVar equal to 'Bar'</button>
  </div>
  <ul class="console" style="list-style-type: none; padding: 0;"></ul>
</div>

I also have a JSFiddle of the demo.

How can I get this to work? Or, is there a different approach I should consider?

1
  • You should use $rootScope as little as possible Commented May 31, 2016 at 20:31

2 Answers 2

1

The whole approach is just totally wrong. How do you expect angular to know that you changed the ngController manually? You have to recompile the h3 element to let angular know about this change:

$compile($document.find('h3'))(Ctrl_1_ViewModel);

A working example: https://jsfiddle.net/cL96m2zx/5/

And on a side note - when you're using Angularjs you should not manipulate the dom from the controller, you need to stop the DOM manipulation and use the build in angular directive instead (or write your own custom directives and manipulate the DOM from there - but as a last resort).

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for providing a solution! Why do you say the approach is totally wrong? What would be a better approach? How can I achieve what I need without modifying the code for the embedded system? If you are referring to $('.console').append as the DOM manipulation, then of course, I would never do that in production. That's just for demonstration purposes only. Again, thanks for your solution! If you could provide a better approach that would be great! Thanks!
Oh, so if you know about this then you're totally fine. I thought you were one of those newbies that still using jQuery to manipulate the DOM while using angularJS.
OK, so that is a solution for the demo I gave. As you can imagine, my app is not nearly this simple and I just wanted to provide enough to reproduce the problem. So in the real app, the variable is an array used with ng-repeat and this solution doesn't seem to work. I've updated the Fiddle. jsfiddle.net/whatisthebigpicture/cL96m2zx/6
@JDavis I suggest that you read about $compile since it looks like you gonna need to use it a lot. I added my example to this specific scenario jsfiddle.net/cL96m2zx/7 and I hope it will help you better understand how to use it
Thanks for the solutions! It turns out that you were right in the first place and my whole approach was just totally wrong. I was able to use the service layer I mentioned in my original post to manipulate the data I needed to without using $controller. This post will still be useful to me in the future and maybe for someone else too. Thanks!
0

You could create a factory that is managing your data and use a watch to update the second controller.

Or it would be also possible with events. Please have a look at the docs. $broadcast or $emit is what you need depending on the direction of your event. Use something like $rootScope.$broadcast('custom:eventName', {data: 'Bar'}) to notify any listener in child scopes like $scope.$on('custom:eventName', function(evt, data) { ... }

I would use events if you want to notify many parts of your app about the change. If you have only two or three different controllers to watch for changes it should be OK with watches on the data.

Please have a look at the demo below or this fiddle.

var app = angular.module('myApp', [])
.factory('sharedData', SharedDataService)
.controller('Ctrl_1', function( $scope , sharedData)
{
	$scope.$watch(function() {
    	return sharedData.getVar('myVar');
    }, function(newVal) {
    	$scope.myFunction(newVal);
    })
	
    $scope.myFunction = function( myParam ) {
        console.log('Ctrl_1: ' + myParam );

        // Demonstrates that the function is called, and with the correct value for myParam.
        $('.console').append('<li>' + myParam + '</li>');

        $scope.myVar = myParam;
      }
  
  //$scope.myFunction('Foo');
});

// sim.js
$('.app-wrapper').attr('ng-controller', 'Ctrl_2');

app.controller('Ctrl_2', function( $scope, $controller, $timeout, sharedData)
{
	//var Ctrl_1_ViewModel = $scope.$new();
  
  //$controller('Ctrl_1', { $scope: Ctrl_1_ViewModel } );
  
  //$timeout( function() { Ctrl_1_ViewModel.myFunction('Bar') } );
  
  $scope.testFunction = function()
  {
  	//Ctrl_1_ViewModel.myFunction('Bar');
    sharedData.setVar('myVar', 'Bar');
    //console.log('Ctrl_2: ' + Ctrl_1_ViewModel.myVar );
  }
});

function SharedDataService() {
	var _privateData = {
    	myVar: 'Foo'
    };
    
	var factory = {
    	setVar: function(name, newVal) {
        	_privateData[name] = newVal;
        },
        getVar: function(name) {
        	return _privateData[name]
        }
    };
    
    return factory;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" class="app-wrapper">
  <div ng-controller="Ctrl_1">
    <h3>myVar = '{{ myVar }}'</h3>
    <button ng-click="testFunction()">Set myVar equal to 'Bar'</button>
  </div>
  <ul class="console" style="list-style-type: none; padding: 0;"></ul>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.