Angularjs custom form control scope is duplicated - javascript

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

Related

How to assign values to isolated scope from ng-change method in controller?

Once user select value from dropdown i have ng-change function invoked onSizeChange and setting values of $scope.maxMb $scope.maxBytes $scope.FileSizeString, So my question how can i use these values in directive once value is selected from dropdown. i tried to bind these values to isolated scope but no luck. Basically i need fileSize and fileValue after size selection that i have added as an attribute to directive in html so these values should bind to isolated scope but that is happening.How can i resolve this problem ?
directive.js
angular.module("App").directive('progressBarCustom', function() {
return {
restrict: 'E',
scope: {
message: "=",
fileSize: "=",
fileValue: "="
},
templateUrl: '/view/partials/progressbar.html',
controller: "StCtrl",
link: function(scope, el, attrs) {
console.log("file size", scope.fileSize);
//these values should assign to directive template once user select value from dropdown
//start
scope.maxMb = scope.fileSize;
scope.maxBytes = 1000 * 1000 * scope.maxMb;
scope.max = scope.maxBytes;
scope.FileSizeString = scope.fileValue;
// end
el.bind('click', function(event) {
scope.$parent.startRecording();
scope.$parent.stopLogs();
scope.$parent.onSizeChange();
console.log('EVENT', event);
});
};
}
});
ctrl.js
$scope.onSizeChange = function() {
$scope.maxMb = $scope.selectedFileSize.size;
$scope.maxBytes = 3000;
$scope.max = $scope.maxBytes;
$scope.FileSizeString = $scope.selectedFileSize.value;
console.log('FileSize', $scope.maxMb);
}
main.html
<div class="col-md-3">
<select class="form-control" ng-model="selectedFileSize" ng-options="item as item.value for item in FileSizeOptions" ng-change="onSizeChange()"><option value="">Select</option></select>
</div>
<progress-bar-custom ng-show="progressBarFlag" message="event" fileSize="selectedFileSize.size" fileValue="selectedFileSize.value"></progress-bar-custom>
template.html
<uib-progressbar type="success" class="progress-striped" max="max" animate="true" value="dynamic"><span>{{downloadPercentage}}%</span></uib-progressbar>
<p class="pull-right bytes-progress-0"><small>Recorded <strong>{{currentBytes}}</strong> of <strong>{{FileSizeString}}</strong></small></p>
Change fileSize to file-size and fileValue to file-value
<progress-bar-custom ng-show="progressBarFlag" message="event" file-size="selectedFileSize.size" file-value="selectedFileSize.value"></progress-bar-custom>
Update after discussion with OP
Pass just selectedFileSize object in the directive instead of sending it as two properties. And you can get values from selectedFileSize.size and selectedFileSize.value inside directive.
And then watch selectedFileSize object in the directive

AngularJS template with brackets in it

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.

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.

Directive Isolate Scope 1.2.2

I'm working with Angular version 1.2.2 for the first time and trying to make a simple directive that uses isolate scope with '=' binding to pass in an object. I've done this a few times before so I'm wondering if maybe there was a change in 1.2.2 that changed this?
Here is my directive:
.directive('vendorSelector', function (VendorFactory) {
return {
restrict: 'E',
replace: true,
scope: { vendorId: '=' },
template: '<select ng-model="vendorId" ng-options="id for id in vendorIds">' +
'<option value="">-- choose vendor --</option>' +
'</select>',
link: function (scope, element, attrs) {
VendorFactory.getVendorIds().then(function(result) {
scope.vendorIds = result;
});
}
}
})
My HTML template using the directive is as follows:
<div class="padding">
<vendor-selector vendorId="someValue"></vendor-selector>
{{ someValue }}
</div>
And the backing controller:
.controller('AddProductController', function($scope, ProductFactory, AlertFactory) {
$scope.vendorId = 0;
$scope.someValue = undefined;
})
I've tried using both $scope.someValue and $scope.vendorId as the supplied object in the html template. In both cases the error I'm getting back is Expression 'undefined' used with directive 'vendorSelector' is non-assignable!. Am I missing something obvious that is preventing these values from being 2-way bound in the isolate scope?
In your html:
<vendor-selector vendorId="someValue"></vendor-selector>
Change vendorId="someValue"
to vendor-id="someValue"
HTML attributes are case insensitive so to avoid confusion Angular converts all camel cased variables (vendorId) to snake case attributes (vendor-id).
So someValue wasn't bound to vendorId. Resulting in vendorId being undefined in the template. And thus your error.

AngularJS The scope for dynamic content through $compile isn't attached to the controller scope

When I generate a new element through a string that has a directive (that's why I need to compile) and that directive generates an association with a variable in the controller scope through "=", the variable in my controller isn't associated to the one in the directive.
I created a jsfiddle to show the example where the "door" ng-model value should be associated to all the directives model values.
See this fiddle: http://jsfiddle.net/aVJqU/2/
Another thing I notice is that the directive that run from elements present in the html show the correct association through the variables (controller and directive).
The html (there is the directive that binds <door>):
<body ng-app="animateApp">
<div ng-controller="tst">
<h2> Controller with its model </h2>
<input ng-model="doorval" type="text"> </input>
{{doorval}}
<h2> Directive render directly from the html </h2>
<door doorvalue="doorval"></door> <key></key>
<h2> Directives that are compiled </h2>
<list-actions actions="actions"></list-actions>
</div>
</body>
This is the directive:
animateAppModule.directive('door', function () {
return {
restrict: "E",
scope: {
doorvalue:"="
},
template: '<span>Open the door <input type="text" ng-model="doorvalue"> </input> {{doorvalue}}</span>',
replace: true
}
})
This is the controller:
var animateAppModule = angular.module('animateApp', [])
animateAppModule.controller('tst', function ($scope, tmplService) {
$scope.doorval = "open"
$scope.actions = tmplService;
})
animateAppModule.service('tmplService', function () {
return [{
form_layout: '<door doorvalue="doorval"></door> <key></key>'
}, {
form_layout: '<door doorvalue="doorval"></door> with this <key></key>'
}]
})
And finally this is the directive that compiles the string that has the directive that doesn't bind:
animateAppModule.directive('listActions', function ($compile) {
return {
restrict: "E",
replace: true,
template: '<ul></ul>',
scope: {
actions: '='
},
link: function (scope, iElement, iAttrs) {
scope.$watch('actions', function (neww, old,scope) {
var _actions = scope.actions;
for (var i = 0; i < _actions.length; i++) {
//iElement.append('<li>'+ _actions[i].form_layout + '</li>');
//$compile(iElement.contents())(scope)
iElement.append($compile('<li>' + _actions[i].form_layout + '</li>')(scope))
}
})
}
}
})
What can I do to bind all the "door" ng-model values together?
Where is the compiled directive binding to?
You just have to pass the doorval reference down through all directives without skip any one. The problem was the listActions directive didn't had access to doorval in its scope.
Check this out: http://jsfiddle.net/aVJqU/5/
#Danypype is basically correct as the problem occurs due to scope isolation, as explained in the documentation.
An alternative solution is to simply eliminate the scope isolation by removing the scope block from within the directive definition.

Categories

Resources