Angular hide dirrective depending on action in another directive - angularjs

I'm new in angular and i'm looking for the best way to do what I want.
In my main page I have 2 directives, one is used to display a button (and maybe other stuff). And another used to display a kind of dialog box/menu.
Each directive has its own controller.
I want to show or hide the second directive when I click on the button in the first one.
I don't really know what are goods or wrong approaches. Should I use a service injected in both controller and set a variable with ng-show in the second directive? This solution doesn't really hide the directive because I need a div inside the directive to hide its content and isn't too much to use a service only for one boolean?
Should I use a kind of global variable (rootscope?) or inject the first controller inside the second one?
Or maybe use a third controller in my main page (used with a service?) or use only one controller for both directive?
Basically without directive I would probably used only one main controller for my whole page and set a variable.

In fact the first directive is just a kind of button used to display "something", and the second directive just a kind of popup waiting a boolean to be displayed. That's why I finally used a service containing a boolean with a getter and a setter to avoid any interaction beetween both controller.
My both controller use this service, the first one to set the value when we click on the element and the second controller provide just a visibility on the getter for my ng-show.
I don't know if it is the best way to do but I am satisfied for now.
Small example here (without directive but with same logic) :
http://codepen.io/dufaux/pen/dXMrPm
angular.module('myModule', []);
angular.module("myModule")
.controller("ButtonCtrl", buttonCtrl)
.controller("PopUpCtrl", popUpCtrl)
.service("DisplayerService", displayerService);
//ButtonCtrl
buttonCtrl.$inject = ["DisplayerService", "$scope"];
function buttonCtrl(DisplayerService, $scope) {
var vm = this;
vm.display = function(){
DisplayerService.setDisplay(!DisplayerService.getDisplay());
}
}
//PopUpCtrl
popUpCtrl.$inject = ["DisplayerService"];
function popUpCtrl(DisplayerService) {
var vm = this;
vm.displayable = function(){
return DisplayerService.getDisplay();
}
}
//Service
function displayerService(){
var vm = this;
vm.display = false;
vm.setDisplay = function(value){
vm.display = value;
}
vm.getDisplay = function(){
return vm.display;
}
}
--
<body data-ng-app="myModule">
<div data-ng-controller="ButtonCtrl as btnCtrl" >
<button data-ng-click="btnCtrl.display()">
display
</button>
</div>
[...]
<div data-ng-controller="PopUpCtrl as popUpCtrl" >
<div data-ng-show="popUpCtrl.displayable()">
hello world
</div>
</div>
</body>

Related

Pass value to Angular controller through ng-include

From a server side code I am loading and angular template as follows:
<div ng-include="'/views/signup.html'" onload="init('premium')"></div>
The view is, for now, the following:
<form ng-controller="AccountSignupController as controller">
</form>
The controller is simply:
function AccountSignupController($scope) {
$scope.plan = '';
$scope.init = function (plan) {
$scope.plan = plan;
console.log(plan);
};
};
On the console plan is always "undefined".
How can I pass a value to the controller through the template?
Do I need to set the controller on ng-include div and use ng-init?
The scope of the controller is inside the form where 'ng-controller' is. Thus $scope.init function is only "available" inside that form.
Both directives should be at the same level. Try moving 'ng-controller' to the div or 'ng-init' to the form (if possible).
You are instantiating your controller using the controller as binding syntax. In that case properties and functions are bound to the this of your controller instead of $scope. Functions should be called using the binding.
Your template should be:
<div ng-include="'/views/signup.html'"
ng-controller="AccountSignupController as controller"
onload="controller.init('premium')"></div>
Your controller:
function AccountSignupController() {
var self = this;
this.plan = '';
this.init = function (initString) {
self.plan = initString;
console.log(initString);
};
};
For more information on the controller as binding syntax, see the AngularJS ngController API

How to update scope variable using service

i have two controllers, the data in the first controller will be updated by click functions in the second controller. I am using service to communicate between controllers and not getting the expected result. whats wrong i am doing
link: http://jsbin.com/hozabigedu/1/
Well, you had several issues. First, I changed your data to be an object and to have a boolean as a property, rather than have itself be a boolean. This is good practice if you want to share info using a service. It's better that you share an object so that different controllers share the same reference. Primitives are problematic for that scenario. So here's the new service:
app.service('testservice', function(){
var data={}; //Changed here
getDataText = function() {
return data;
};
setDataText= function(val) {
data.text = val; //And changed here
return data;
};
return {
getDataText: getDataText,
setDataText: setDataText
};
});
Another issue is that your controller called the wrong function, it should be getDataText():
$scope.inputText = testservice.getDataText();
And lastly, you forgot to close the divs of the click elements, so the click 2 events bubbled up to the click 1 events thus showing right after hiding, so replace the 2nd controller div with this one:
<div ng-controller="secondController">
<div ng-click="showDiv();">click1</div>
<br/>
<br/>
<div ng-click="hideDiv();">click2</div>
</div>
</div>

How do I load an external HTML file and have the scope variables renderred properly in angularJS?

What I am trying to do is after clicking on a buddy in the buddy list, load a chat dialog template HTML file without disturbing other elements in current DOM just like chatting in facebook.
My problem is that after loading the html template file the scope variables such as {{contact.jid}} are not properly rendered, and the controller for the dialog is not even called.
How can I force a rerender or a call on the controller so that those variables are properly renderred? Or should I not use the jQuery.load function to do this? I can't figure out any other way.
Thank you.
Code of the controllers:
// Controller for the chat dialog
ctrl.controller('ChatCtrl',function($scope){
$scope.test = "Test"
});
// Controller for the buddy list
// $scope.toggleDialog is called when a buddy is clicked
ctrl.controller('ContactCtrl',function($scope){
$scope.contacts = window.contactList;
$scope.toggleDialog = function(to){
$.("#chatarea").load("chatdialog.html")
};
});
The controller function of chat dialog is not called after loading chatdialog.html which has an attribute of ng-controller, so the {{test}} variable is not set.
You need to wrap your content that will be compiled inside a directive.
This directive receives the variables and the HTML that will be compiled.
You can wrap this directive in the element that you like:
http://plnkr.co/edit/RYVCrlDmgVXT3LszQ9vn?p=preview
For example, given the following html and variables:
// In the controller
$scope.controllerHtml = '<span>{{variables.myVariable}}</span>';
$scope.controllerVariables = {myVariable: 'hello world'};
<!--In the HTML-->
<div compile-me html-to-bind="controllerHtml" variables="controllerVariables"></div>
You will get the following:
<div>
<span>hello world</span>
</div>
You are loading the external HTML via jQuery and Angular has no way of knowing how to use it. There are two ways to solve this issue:
use ngInclude to load the template from the server, angular will load it from the server and compile it for you to use.
continue to use jQuery load the HTML from the server and use the $compile service to teach angular how to use it
I would highly suggest using method #1 to load your external template.
I suppose the:
$.("#chatarea").load("chatdialog.html")
is the jQuery .load, or something similar. I would get the template via ngInclude, checking if test is setted or not; html:
<div id="chatarea" ng-if="test">
<div ng-include="'chatdialog.html'"/>
</div>
controller:
ctrl.controller('ContactCtrl',function($scope){
$scope.contacts = window.contactList;
$scope.test = '';
var callbackFunction = function(data) {
$scope.test = data.test;
};
$scope.toggleDialog = function(to){
AjaxRequestToGetBuddyInfoAndMessages(to, callbackFunction);
};
});
Obviously test will be a more complex object, so the ngIf test will be different (and you will need to take into account the fact that:
$scope.test = data.test
if they are objects, they will lose the reference and have an unwanted behaviour).

AngularJs can't access form object in controller ($scope)

I am using bootstrap-ui more specifically modal windows. And I have a form in a modal, what I want is to instantiate form validation object. So basically I am doing this:
<form name="form">
<div class="form-group">
<label for="answer_rows">Answer rows:</label>
<textarea name="answer_rows" ng-model="question.answer_rows"></textarea>
</div>
</form>
<pre>
{{form | json}}
</pre
I can see form object in the html file without no problem, however if I want to access the form validation object from controller. It just outputs me empty object. Here is controller example:
.controller('EditQuestionCtrl', function ($scope, $modalInstance) {
$scope.question = {};
$scope.form = {};
$scope.update = function () {
console.log($scope.form); //empty object
console.log($scope.question); // can see form input
};
});
What might be the reasons that I can't access $scope.form from controller ?
Just for those who are not using $scope, but rather this, in their controller, you'll have to add the controller alias preceding the name of the form. For example:
<div ng-controller="ClientsController as clients">
<form name="clients.something">
</form>
</div>
and then on the controller:
app.controller('ClientsController', function() {
// setting $setPristine()
this.something.$setPristine();
};
Hope it also contributes to the overall set of answers.
The normal way if ng-controller is a parent of the form element:
please remove this line:
$scope.form = {};
If angular sets the form to your controllers $scope you overwrite it with an empty object.
As the OP stated that is not the case here. He is using $modal.open, so the controller is not the parent of the form. I don't know a nice solution. But this problem can be hacked:
<form name="form" ng-init="setFormScope(this)">
...
and in your controller:
$scope.setFormScope= function(scope){
this.formScope = scope;
}
and later in your update function:
$scope.update = function () {
console.log(this.formScope.form);
};
Look at the source code of the 'modal' of angular ui bootstrap, you will see the directive has
transclude: true
This means the modal window will create a new child scope whose parent here is the controller $scope, as the sibling of the directive scope. Then the 'form' can only be access by the newly created child scope.
One solution is define a var in the controller scope like
$scope.forms = {};
Then for the form name, we use something like forms.formName1. This way we could still access it from our controller by just call $scope.forms.formName1.
This works because the inheritance mechanism in JS is prototype chain. When child scope tries to create the forms.formName1, it first tries to find the forms object in its own scope which definitely does not have it since it is created on the fly. Then it will try to find it from the parent(up to the prototype chain) and here since we have it defined in the controller scope, it uses this 'forms' object we created to define the variable formName1. As a result we could still use it in our controller to do our stuff like:
if($scope.forms.formName1.$valid){
//if form is valid
}
More about transclusion, look at the below Misco's video from 45 min. (this is probably the most accurate explanation of what transcluded scopes are that I've ever found !!!)
www.youtube.com/watch?v=WqmeI5fZcho
No need for the ng-init trickery, because the issue is that $scope.form is not set when the controller code is run. Remove the form = {} initialization and get access to the form using a watch:
$scope.$watch('form', function(form) {
...
});
I use the documented approach.
https://docs.angularjs.org/guide/forms
so, user the form name, on "save" click for example just pass the formName as a parameter and hey presto form available in save method (where formScopeObject is greated based upon the ng-models specifications you set in your form OR if you are editing this would be the object storing the item being edited i.e. a user account)
<form name="formExample" novalidate>
<!-- some form stuff here -->
Name
<input type="text" name="aField" ng-model="aField" required="" />
<br /><br />
<input type="button" ng-click="Save(formExample,formScopeObject)" />
</form>
To expand on the answer by user1338062: A solution I have used multiple times to initialize something in my controller but had to wait until it was actually available to use:
var myVarWatch = $scope.$watch("myVar", function(){
if(myVar){
//do whatever init you need to
myVarWatch(); //make sure you call this to remove the watch
}
});
For those using Angular 1.5, my solution was $watching the form on the $postlink stage:
$postLink() {
this.$scope.$watch(() => this.$scope.form.$valid, () => {
});
}

Modify $rootscope property from different controllers

In my rootscope I have a visible property which controls the visibility of a div
app.run(function ($rootScope) {
$rootScope.visible = false;
});
Example HTML:
<section ng-controller='oneCtrl'>
<button ng-click='toggle()'>toggle</button>
<div ng-show='visible'>
<button ng-click='toggle()'>×</button>
</div>
</section>
Controller:
var oneCtrl = function($scope){
$scope.toggle = function () {
$scope.visible = !$scope.visible;
};
}
The above section works fine, the element is shown or hide without problems. Now in the same page in a different section I try to change the visible variable to show the div but it doesn't work.
<section ng-controller='otherCtrl'>
<button ng-click='showDiv()'>show</button>
</section>
Controller:
var otherCtrl = function($scope){
$scope.showDiv = function () {
$scope.visible = true;
};
}
In AngularJS, $scopes prototypically inherit from their parent scope, all the way up to $rootScope. In JavaScript, primitive types are overwritten when a child changes them. So when you set $scope.visible in one of your controllers, the property on $rootScope was never touched, but rather a new visible property was added to the current scope.
In AngularJS, model values on the scope should always "have a dot", meaning be objects instead of primitives.
However, you can also solve your case by injecting $rootScope:
var otherCtrl = function($scope, $rootScope){
$scope.showDiv = function () {
$rootScope.visible = true;
};
}
How familiar are you with the concept of $scope? It looks to me based on your code that you're maintaining two separate $scope variables called "visible" in two different scopes. Do each of your controllers have their own scopes? This is often the case, in which case you're actually editing different variables both named "visible" when you do a $scope.visible = true in different controllers.
If the visible is truly in the rootscope you can do $rootScope.visible instead of $scope.visible, but this is kind of messy.
One option is to have that "otherCtrl" code section in a directive (you should probably be doing this anyway), and then two-way-bind the directive scope to the parent scope, which you can read up on here. That way both the directive and the page controller are using the same scope object.
In order to better debug your $scope, try the Chrome plugin for Angular, called Batarang. This let's you actually traverse ALL of your scopes and see the Model laid out for you, rather than just hoping you're looking in the right place.

Resources