AngularJS template with brackets in it - javascript

Why AngularJS doesn't accept brackets inside a ng-template content? I need it to create an input that's going to be an array, but I get this error:
"Error: Syntax Error: Token ']' not a primary expression at column 15 of the expression [form.interval[]] starting at []]."
angular.module("main", []).controller("MyCtrl", function($scope) {
}).directive("ngPortlet", function ($compile) {
return {
template: '<input class="form-control" type="text" placeholder="Interval" ng-model="form.interval[]" />',
restrict: 'E',
link: function (scope, elm) {
scope.add = function(){
console.log(elm);
elm.after($compile('<ng-portlet></ng-portlet>')(scope));
}
}
};
});
<div ng-app="main">
<div ng-controller="MyCtrl">
<div id="container">
<button ng-click="add()" >Add</button>
<ng-portlet></ng-portlet>
</div>
</div>
</div>
jsfiddle:
http://jsfiddle.net/7kcrrapm/1/

EDIT:
Now that I better understand what you're trying to accomplish, here is a different approach:
angular.module("main", []).controller("MyCtrl", function($scope) {
}).directive("ngPortlet", function ($compile) {
return {
template: '<input class="form-control" type="text" placeholder="Interval" ng-model="interval" />',
restrict: 'E',
link: function (scope, elm) {
var intervals = [];
scope.add = function(){
intervals.push(parseInt(scope.interval, 10));
console.log(intervals);
}
}
};
});
Now you have access to an array (intervals) that contains a list of all intervals added.
ORIGINAL:
form.interval[] is not valid JavaScript and thus not a valid scope property. If you need the property to be an array you can simply declare it in your controller ("MyCtrl"):
$scope.form.interval = [];
If you don't create the scope property in the controller your self, it will be implicitly created by the ng-model directive. You can find more info in the docs. I might also suggest this great read about Scopes in the official Angular Wiki

From what I understand, what you really want is ng-repeat.
<span ng-repeat="hour in form.interval">
<input class="form-control" type="text" placeholder="Interval" ng-model="hour" />
</span>
Declare the variable inside the controller or directive:
$scope.form.interval = [];
When you do add() to get another input, add a blank entry to the array in the controller or directive:
$scope.form.interval.push('');
Call add() when you create the variable if you want to start with one empty input box.

The reason it's not working, is because [] is invalid JavaScript syntax on a variable reference.
interval = [1, 2, 3]; // Ok.
interval = []; // Also Ok.
var foo = interval[]; // This isn't valid!
Take those square brackets off, or if you're wanting to do a ng-repeat setup you might consider some of the other given answers.

Related

Add new HTML content with different values in Angularjs

I created a directive to add some content whenever the button "add" is clicked. But I don't know how to get all values in these new inputs.
HTML Code
angular.module('myApp', [])
.controller('myController', function() {
})
.directive('addContent', function($document, $compile) {
return {
restrict: 'A',
replace: false,
link: function(scope, element, attr) {
element.on('click', function() {
var newcontent = '<input type="text" ng-model="myModel"><br>';
angular.element($document[0].querySelector('.newcontent')).append($compile(newcontent)(scope));
})
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="newcontent" ng-app="myApp" ng-controller="myController">
<button type="button" add-content>Add</button><br><br>
</div>
So, how can I set a different ng-model value to each one new input that is created and, how can I get this values in my controller?
You could go with something like this:
The Idea:
A base name can be defined from the html where the directive is being applied.
An incremental number is used in the directive when creating new inputs (the view controller (programmer) where this model is used must be aware of that). Actually, you could use any other strategy you'd prefer better in this case. I've used this one for simplicity and stating my point.
The code (see below snippet):
In the directive
Create a counter for incrementing as new inputs are added: var count = 0;
Take the base name specified in the html with var model = scope.$eval(attr['addContent']);
Modify the newcontent variable to use that base name and the incremental counter like this: var newcontent = '<input type="text" ng-model="' + model + (count++) + '"><br>';
The controller
For organization, create a variable for holding the base name: $scope.buttonModel = 'buttonModelReference';
Access the value of those new models like this: $scope[$scope.buttonModel + $scope.index] where $scope.index is the index of the input (where 0 is the first input created)
The view
Use the modified directive like this add-content="buttonModel" where buttonModel is the variable defined in the controller.
Plus code (for demonstration purposes only)
The showModel function shows the value of one (dynamic created) input passing as reference the index of the input (0 zero is the index of the first input created)
The Snippet
angular.module('myApp', [])
.controller('myController', function($scope) {
$scope.index;
$scope.buttonModel = 'buttonModelReference';
$scope.showModel = function() {
console.log($scope[$scope.buttonModel + $scope.index]);
}
})
.directive('addContent', function($document, $compile) {
var count = 0;
return {
restrict: 'A',
replace: false,
link: function(scope, element, attr) {
element.on('click', function() {
var model = scope.$eval(attr['addContent']);
var newcontent = '<input type="text" ng-model="' + model + (count++) + '"><br>';
angular.element($document[0].querySelector('.newcontent')).append($compile(newcontent)(scope));
})
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="newcontent" ng-app="myApp" ng-controller="myController">
<button type="button" ng-click="showModel()">showModel</button> <input ng-model="index" type="number" placeholder="select the model index starting from 0" /> <br><br>
<button type="button" add-content="buttonModel">Add</button><br><br>
</div>

How to show different value of input element with ng-model?

In the controller if have a variable that tracks the index (starting at 0) of the page for a pagination table:
var page {
pageNumber: 0;
}
Question: how can I show this pageNumber variable in the html, but always incremented by +1? (as the index=0 page is obviously the 1st page and should thus be shown as Page 1)
<input type="text" ng-model="page.pageNumber">
Also, when the model gets updated, the value in the input should automatically change (again: also incremented by +1).
I think this is a use-case for $formatters and $parsers. They operate on the model's property and there is no need to create a dummy property on the model. Documentation here. Please correct me if this is not the use case for $formatters and $parsers.
Please see below.
HTML markup
<body ng-app="app" ng-controller="mainCtrl">
{{page}}
<input paginated-index type="text" ng-model="page">
</body>
js
var app = angular.module('app', []);
app.controller('mainCtrl', function($scope) {
$scope.page = 0;
});
app.directive('paginatedIndex', function()
{
return{
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModelController)
{
ngModelController.$formatters.push(function(value)
{
return value+1;
})
ngModelController.$parsers.push(function(value)
{
return value-1;
})
}
}
});
In your controller, change your page object to this:
$scope.page = {
displayedPage: function(num) {
if(arguments.length) {
$scope.page.pageNumber = num - 1;
return num;
} else {
return $scope.page.pageNumber + 1;
}
},
pageNumber: 0
}
And then yourelement to this:
<input type="text" ng-model="page.displayedPage" ng-model-options="{ getterSetter: true}" />
This will display the page number plus 1, but leave the actual page.pageNumber variable the way it should be.
The getterSetter: true options I've added in will bind the model to a getter/setter function, which allows you to pass in the argument - in this case, your entered page number - and return from that function. You can read more information on this in the documentation for ngModel
you can try using something like this.
$scope.data=$scope.page.pageNumber+1;
$scope.fuc=function(){
$scope.page.pageNumber=$scope.data-1;
};
and your Html will be like
<input type="text" ng-model="data" ng-change="fuc()" >
check this plunk Plunker

Angularjs- adding/removing dynamic html elements (dropdown)

here is my code-
http://plnkr.co/edit/oTWXbLIKOxoGTd4U0goD?p=preview
why is the days dropdown does not data bind with scope.demoDays, it is always empty?
is this the correct way to add dropdown dynamically? If user adds 5 dropdown, how to get the results , will ng-model="selectedDay" create an array of selection? any suggestions?
Thank you
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $compile) {
var counter = 0;
$scope.fields = [];
$scope.days =['Day','Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
$scope.addField = function() {
$scope.fields.push({name:"test " + counter++});
};
});
app.directive('demoDisplay', function($compile){
return {
scope:{
demoDisplay:"=", //import referenced model to our directives scope
demoDays:"="
},
link:function (scope, elem, attr, ctrl)
{
scope.$watch('demoDisplay', function(){ // watch for when model changes
elem.html("") //remove all elements
angular.forEach(scope.demoDisplay, function(d){ //iterate list
var s = scope.$new(); //create a new scope
angular.extend(s,d); //copy data onto it
console.log(scope.demoDays);
var template = '<label class="item item-input"><div class="style-select"><select ng-model="selectedDay" ng-options="day for day in scope.demoDays"></select><br></div></label>';
elem.append($compile(template)(s)); // compile template & append
});
}, true) //look deep into object
}
}
})
html
<button ng-click="addField()">Add Field</button>
<div demo-display="fields" demo-days="days"></div>
There is no need for $watch in your link function - you have already established two-way binding by specifying = on your scope property. And you can use a plain template, without having to compile.
templateUrl: 'template.html',
where template.html is:
<label class="item item-input">
<div class="style-select">
<select ng-model="demoDisplay.selection" ng-options="day for day in demoDays"></select>
<br>
</div>
</label>
Notice that the select is bound to demoDisplay.selection, which will be created on each field and be accessible on the parent scope via two-way binding. Also, note that within ng-options, I changed scope.demoDays to just demoDays. In a directive's template you only need to use the property's name to access a scope value.
You can use the directive inside ng-repeat to create additional fields when the button is clicked:
<div ng-repeat="field in data.fields">
<div demo-display="field" demo-days="days"></div>
</div>
Here is a working plunker: http://plnkr.co/edit/pOY0l18W7wEbfSU7DKw2?p=preview
Any easy fix to get it working.
In your var template you have scope.demoDays.
Simply change this to demoDays. You are already in this scope so using it again isn't necessary.

How do I use the '&' correctly when binding in a directive in Angularjs?

I have having trouble with understanding the & option in binding scope properties. I have read these tutorials: angularjs-directives-using-isolated-scope-with-attributes, practical-guide-angularjs-directives and the-hitchhikers-guide-to-the-directive as well as looking at the documentation but I am completely confused about how to use the & symbol.
Here is my attempt at using it:
var app = angular.module('directivesApp');
app.directive('simplyIsolated', function () {
return{
restrict: 'EA',
replace: true,
scope:{
attnum: '#numone'
,bindnum: '=numtwo'
,expressnum: '&sq'
},
link: function (scope, elem, attr){
scope.x = scope.expressnum();
},
template:'<div><p> using "#" = {{attnum+attnum}}</p>'+
'<p>using "=" {{bindnum+bindnum}}</p>'+
'<p>using "&" {{x}}</p><br/><p>{{y}}</p>'+
'</div>'
};
})
.controller('MainCtrl', function ($scope) {
$scope.sqr = function(num){
return num*num;
}
});
and this is my html
<div class="container" ng-controller="MainCtrl">
<input type="text" ng-model="num1parent" />
<input type="number" ng-model="num2parent" />
<input type="number" ng-model="num3parent" />
<p>Parent Scope # {{usernameparent}},
= {{userageparent}},
& = {{sqr(num3parent)}}
</p>
<div simply-isolated numone='{{num1parent}}' numtwo='num2parent' sq="sqr"></div>
</div>
This is the result.
the first two inputs are used to show the difference between # and =. The third input is used to show the sqr() method works but in the text underneath the using "&" is supposed to be the square of the 2nd input but I don't get any result or error
If someone could point me in the right direction I would really appreciate it
Also: Why would you use the & over scope.$parent?
Simply because you're not using the function scope.x as a function instead you have it evaluated as a mere function expression with no argument value.
try changing the template to this:
template:'<div><p> using "#" = {{attnum+attnum}}</p>'+
'<p>using "=" {{bindnum+bindnum}}</p>'+
'<p>using "&" {{x(bindnum}}</p><br/><p>{{y}}</p>'+
'</div>'
that should show something when you provide changes in the num2parent model.
Why would you use the & over scope.$parent?
Because it is normally not recommended to do so, if you would be doing it in such a manner it might be better not to isolate the scope of your directive and have direct access to the parent controllers properties in the directive.
Update:
The reference passed by the attribute notiation '&' is a function that returns the function defined in your controllers. To invoke the function reference, simply do this:
{{expressnum()(bindNumb)}}
that will do the trick.
Note:
There is a reason for this, functions often passed via '&' attribute notations are function callbacks such as events. So if your exressnum() was an callback function for an event then it would have been used like this in you template perhaps
<a ng-click="expressnum()">Click Expression</a>
Furthermore, the function sqr() is a type of function that changes the result of an expression, such functions must be defined as a filter.

Angularjs custom form control scope is duplicated

I have created a simple angularjs directive to input city and zip codes. It works fine except that if I used it twice within the same controller the values in the input field are duplicated!
I believe it's a scope problem but I don't know how to solve it?
FDVilleModule = angular.module('FDVille', []).
directive('fdVille', () ->
return {
restrict: 'E'
require: 'ngModel'
template: """
<div class=\"row-fluid\">
<div class=\"span4\">
<input
ng-model=\"cp\"
ng-change=\"edit()\"
maxlength=\"5\"
type=\"text\"
class=\"input-block-level\"
placeholder=\"Code Postal\" />
</div>
<div class=\"span8\">
<select ng-model=\"selected_ville\"
ng-options=\"v.id as v.nom for v in villes\"
class=\"input-block-level\">
</select>
</div>
</div>"""
link: (scope, elem, attr, ctrl) ->
scope.$watch('selected_ville', (value)->
ctrl.$setViewValue(value))
controller: ($scope) ->
download_villes = (cp) -> $.getJSON('/ws/villes/cp', {cp:cp}, set_data)
download_villesid = (id) -> $.getJSON('/ws/villes/id', {id:id}, set_init_data)
set_data = (results) ->
$scope.villes = results
$scope.selected_ville = results[0].id if results.length
$scope.$apply()
saved_cp = ""
$scope.edit = () ->
if isNaN($scope.cp)
$scope.cp = saved_cp
else
saved_cp = $scope.cp
if saved_cp.length == 5
download_villes(saved_cp)
else
$scope.selected_ville = null
$scope.villes = []
}
)
Actually I found the answer in the docs:
scope - If set to:
true - then a new scope will be created for this directive. If
multiple directives on the same element request a new scope, only one
new scope is created. The new scope rule does not apply for the root
of the template since the root of the template always gets a new
scope.
In some other instance, you could try
replace: true
Note: you need to have one root element.
Here's the link to the documentation for us readers who didn't know:
http://docs.angularjs.org/guide/directive

Categories

Resources