Add new entry to (empty) scope array using ng-click directive - arrays

My scope does not contain an "notYetExistingArray" at the time of generation.
Via a button, I would like to add the array plus a first entry to it.
Each subsequent push of the same button should then add another entry.
Unfortunately, I have no clue on how to approach it.
Currently I have:
<button type="button" ng-click="notYetExistingArray.unshift({})">
If the scope already contains an object "notYetExistingArray" of type array = [] I can easily add an entry with above function.
Any advise on how to do it?

Call a controller function from your ng-click directive rather than trying to do all that stuff in your markup. It can include a check for the existence of the array, and create it if needed.
<button type="button" ng-click="addThis(thing)">
In the controller:
ctrl.addThis = function(thing) {
if (ctrl.myArray === undefined) {
ctrl.myArray = [];
}
myArray.unshift(thing);
};
Note that I'm using controllerAs syntax here, so ctrl might be $scope instead.

You can initialize the array before doing that, for example:
<button type="button" ng-init="notYetExistingArray=[]" ng-click="notYetExistingArray.unshift({})">
check the documentation for ng-init https://docs.angularjs.org/api/ng/directive/ngInit

A good way is to bind a function on the ngClick directive, which will perform a test in order to check if the array exist.
In your controller :
function foo(item) {
//if my array does not exist
if (!$scope.myArray) {
//create the array
$scope.myArray = [];
}
//Here, we can add some data to our array, we are sure that the array exist
$scope.myArray.unshift(item);
}
$scope.foo = foo;
Html :
<h2>Array.length : {{myArray.length}}</h2>
</br>
<button type="button" ng-click="foo({})">Add</button>
Feel free to check the Working Plunker

Related

Angular: how to save edited values?

here is my plnkr
http://plnkr.co/edit/0W1SZll1BOK5uuEtdycy?p=preview
im trying to save edited values from my edit contact modal.
here is a edit method that takes current value from object:
$scope.edit = function(terminal) {
$scope.name = terminal.name;
$scope.last = terminal.last;
$scope.age = terminal.age;
}
i read about angular copy, there is anything else?
any one can help me please ill glat to learn how to do that
Use angular.copy when assigning value of object or array to another variable and that object value should not be changed.
Without deep copy or using angular.copy, changing value of property or adding any new property update all object referencing that same object. Your JS will look like this:
$scope.edit = function(terminal) {
$scope.name = angular.copy(terminal.name);
$scope.last = angular.copy(terminal.last);
$scope.age = angular.copy(terminal.age);
}
You are creating a lot of extra manual work by not using one object in ng-model in the first place instead of using individual scope properties.
There is a golden rule also that if you don't have a dot in ng-model you are doing it wrong
Do something like:
<input ng-model="terminal.name">
<input ng-model="terminal.age">
Then work along the following lines:
$scope.edit = function(terminal) {
// store reference to terminal being edited
$scope.currentTerminal = terminal;
// make a copy so live one is not affected -- will be used in form
$scope.terminal = angular.copy(terminal);
}
$scope.save = function(){
// update stored original with changes
angular.extend($scope.currentTerminal, $scope.terminal);
// reset terminal used in form
$scope.terminal ={};
}
I would strongly suggest you get rid of jQuery and bootstrap.js and use angular-ui-bootstrap insted
You need to pass the index of the row that you are going to edit. pass the index when you click on edit button.
Change in script.js
$scope.edit = function(terminal,index) {
$scope.name = terminal.name;
$scope.last = terminal.last;
$scope.age = terminal.age;
$scope.edit_index = index
}
$scope.saveEdit =function(){
index = $scope.edit_index
$scope.terminals[index].name = $scope.name;
}
Change in index.html
<td> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#editModalLong" ng-click="edit(terminal,$index)">Edit</button>
<button type="button" class="btn btn-primary" data-dismiss="modal" ng-click="saveEdit()">Save</button>
http://plnkr.co/edit/GpVB2dUiHCY6NslQDuBe?p=preview

Angular hide dirrective depending on action in another directive

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>

AngularJS - calculate value for each field

I have list of fields(todos) with button on their left. I want to click on one of the buttons so it calculate the value for this specific field.
What I'm getting is that clicking on specific button calculate and show the same result for all the fields
this is what I wrote :
<h1 ng-show="myVar">
<ul>
<li ng-repeat="todo in todos">
<button ng-show="!editing[$index]" ng-click="edit($index)">click</button>
<h2> Result is:{{result}}</h2>
{{todo.name}}
</li>
</ul>
</h1>
and the controller
$scope.edit = function(index){
var todo = $scope.todos[index];
var counter = 0;
angular.forEach($scope.todos,function(value,index){
if (todo.topic == 'important')
{counter = counter+1;}
})
$scope.result = counter;
}
What am i doing wrong?
Basically result variable of scope you have used for binding is not the same scope variable which has been defined inside controller scope.
Because ng-repeat does work in quite a different way, when it render a DOM by looping through provided collection(here its todos), it creates a new scope for each iteration which is prototypically inherited from it parent scope.
Do use Dot Rule while defining model, so that it would follow prototypal inheritance,
$scope.model = {};
//then use
$scope.model.result = result;
HTML
<h2> Result is:{{model.result}}</h2>
Other way around to sort this issue out would be using controllerAs approach while defining controller, but in that case you need to get read of $scope & should replace it with this context of controller function.
You use the same index variable name as a function input parameter - function(index) and in forEach loop ,function(value,index){ - and it gets overwritten.
//here
$scope.edit = function(index){
var todo = $scope.todos[index];
var counter = 0;
//and here
angular.forEach($scope.todos,function(value,index){

How to use a dynamically generated value in a template in AngularJS

I have a custom form application written in AngularJS and now I need to use the data from the form in a template. But nothing I've tried seems to work.
I am creating a custom directive like this...
.directive('dynamicModel', ['$compile', function ($compile) {
return {
'link': function(scope, element, attrs) {
scope.$watch(attrs.dynamicModel, function(dynamicModel) {
if (attrs.ngModel == dynamicModel || !dynamicModel) return;
element.attr('ng-model', dynamicModel);
if (dynamicModel == '') {
element.removeAttr('ng-model');
}
// Unbind all previous event handlers, this is
// necessary to remove previously linked models.
element.unbind();
$compile(element)(scope);
});
}
};
}])
This is attached to a form element like this..
<div class="row" ng-repeat="field in customForm.fields">
<label>{{field.displayname}}
<input class="form-control" type="{{field.type}}" name={{field.variable}} dynamic-model="field.databind" placeholder="{{field.variable}}" required="{{field.isRequired}}"></label></div>
This part works great, the field is now 2 way bound to the input form.
However when I later tried to use the same method to show the value in a report computed from the form, I get "field.databind" or at best the resolved databind field name such as "currentUser.name" rather than the value, e.g. "devlux"
I've tried
<div class="row" ng-repeat="field in customForm.fields">
<p>{{field.name}} = {{field.databind}}</p>
Also
<p dynamicModel="field.databind"></p>
</div>
Nothing works unless I put it into an input element, which isn't what I'm trying to do here.
The dynamic model code was pulled off someone elses answer to a question about creating dynamic form elements, and honestly I think it's just a step beyond my comprehension. But assuming that "field.databind" will always be a string literal containing the name of an inscope model, how on earth do I access it in a normal template?
{{field.databind}} will be evaluated against the current $scope and will result in whatever $scope.field.databind is, for example the string currentUser.name.
Angular has no way of knowing that currentUser.name isn't the string you want, but actually another expression that you want to evaluate.
To evaulate it again you will need to add a function to your $scope that uses the $parse service.
For example:
$scope.parseValue = function (value) {
return $parse(value)($scope);
};
In HTML:
<div class="row" ng-repeat="field in customForm.fields">
<p>{{field.displayname}} = {{parseValue(field.databind)}}</p>
</div>
The argument that gets passed to parseDynamicValue will for example be currentUser.name. Then it uses the $parse service to evaulate the expression against the current $scope, which will result in for example devlux.
Demo: http://plnkr.co/edit/iPsGvfqU0FSgQWGwi21W?p=preview

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, () => {
});
}

Resources