Dynamically setting ng-model is breaking - javascript

I have a directive HTML template like this:
textinput.html
<label for="{{name}}">{{label}}</label>
<input type="{{type}}" placeholder="{{placeholder}}" id="{{name}}" ng-model="{{name}}">
The label is outputting correctly, however everything inside of the input field is outputing as static {{varname}}
If I remove the ng-model like this:
<input type="{{type}}" placeholder="{{placeholder}}" id="{{name}}">
It outputs the variables correctly, but the moment I place the ng-model in and dynamically assign it a value, it breaks the entire input.
Why is this happening?
My goal is to be able to create simple 1 line text inputs which I can mass change with the directive, like so:
<textinput name="username" label="Username" type="text" placeholder="Enter Username"></textinput>

Finally figured it out, you have to pass the ng-model through using 2 way binding like this:
<textinput type="email" name="userEmail" ng-model="userEmail" placeholder="Email Address..." label="Email"></textinput>
-
app.directive('textinput', function() {
return {
restrict: 'E',
scope: {
label:'#label',
placeholder:'#placeholder',
name: '#name',
type: '#type',
ngModel: '=ngModel'
},
transclude: true,
templateUrl: 'directives/textinput.html'
};
});
Now within the template HTML file you can map properties without the {{ }} which works with ng-model.
example:
<label for="{{name}}">{{label}}</label>
<input type="{{type}}" placeholder="{{placeholder}}" id="{{name}}" ng-model="ngModel">
Now it'll be super easy to quickly generate re-usable form code.

Related

Form element directive with error message manage AngularJS

I'm trying to create a input directive element with validation.
I'd like to manage error status in this directive.
There are 3 files
Index.html: uses this directive
textValid.js: contains directive code
textValid.html: contains the directive template
I create this directive
textValid.js
App.directive("textValid", function() {
return {
restrict: "E",
templateUrl: "tpl/textValid.html",
require: "?ngModel",
scope: {
name: "#",
element: "=",
model: "="
}
};
});
index.html
<form name="edit_form_ctrl.contract_edit_form" action="#" novalidate >
<div class="row">
<text-valid name="ncontract"model="edit_form_ctrl.contract.ncontract"
element="edit_form_ctrl.contract_edit_form.ncontract">
</text-valid>
</div>
</form>
and template textValid.html
<input type="text" name="name" ng-model="model" class="form-control" ng-required="true" value="{{model}}" />
<div>pristine: {{element.$pristine}}</div> <!--is always undefined-->
<div>Invalid: {{element.$error}}</div> <!--is always undefined-->
<span class="color-red" ng-if="element.$error.required &&!element.$pristine">
{{curLang.field_mandatory}}
</span>
I'm trying to get input control to check $error and $pristine value, but I cannot to achieve it.
I read all documentation and the book too, but with any results.
Does someone try to do that?
Thanks in advance
If you add {{double-curlies}} around the name attribute of the input element like so:
<input type="text" name="{{name}}" ng-model="model" class="form-control" ng-required="true" />
You should be able to access the ngModelController for it from inside text-valid with $scope.form[$scope.name]
A couple other notes:
using a value attribute on and element with ng-model attribute will not have an effect.
you have require: '?ngModel' in your directive definition, but there is no ng-model attribute on the text-valid element. This is ok, but you won't get any ngModelController being injected unless the text-valid element has an ng-model attribute.
Edit:
Because text-valid has isolate scope, to access the FormController, you would need to require it and bind it:
App.directive("textValid", function() {
return {
restrict: "E",
templateUrl: "tpl/textValid.html",
require: "^^form",
scope: {
name: "#",
element: "=",
model: "="
},
link: function($scope, elem, attr, FormCtrl){
$scope.form = FormCtrl;
// now you should be able to access $scope.form[$scope.name]
// (you *might* need to put any initialization that accesses it inside a $timeout to wait for it to be rendered/bound)
}
};
});
In template, you should then be able to access it like:
<div>pristine: {{form[name].$pristine}}</div>
<div>Invalid: {{form[name].$error}}</div>
Try this:
<input type="text" name="name" ng-model="model" class="form-control" ng-required="true" value="{{model}}" />
<div>pristine: {{form.name.$pristine}}</div> <!--is always undefined-->
<div>Invalid: {{form.name.$error}}</div> <!--is always undefined-->
<span class="color-red" ng-if="element.$error.required &&!element.$pristine">
{{curLang.field_mandatory}}
</span>

How to do name="skills[]" in angularjs in input type text?

In php usually we are doing name="skills[]" to get data from form as array but how to do this in angularjs?
PHP example:
<input type="text" name="skills[]" />
I want to do the same in AngularJS but getting syntax error.
I am trying like this:
My code:
<input type="text" name="skillname" required data-ng-model="c.skills[].skillname" class="small">
<input type="text" name="skillname" required data-ng-model="c.skills[].skiilname" class="small">
Need help. Thanks.
In angular, data rules your UI, not the other way around. You should have skills array in your controller
app.controller('MainCtrl', function($scope) {
$scope.skills = [
{},
{},
{}
];
});
which you'll feed to ng-repeat or something.
<div ng-repeat='skill in skills'>
<input ng-model='skill.name' />
</div>
This way, adding new textfields on the page is as easy is pushing a new element to the array, $scope.skills.push({}). No need to create DOM elements or anything. This is the angular way.
Demo: http://plnkr.co/edit/Uqldnst3gnF07SJGV6Bn?p=preview
try this:
your controller is:
app.controller('MainCtrl', function($scope) {
$scope.skills = [];
$scope.formData={
skillname:[]
};
});
and your html is:
<div ng-repeat="skill in skills">
<input type="text" required ng-model="formData.skillname[$index]" ng-init="formData.skillname[$index]=skill.skillname" class="small">
</div>
and without ng-repeat you can use this code:
<input type="text" required ng-model="skillname[0]" ng-init="skillname[0]=skills[0].skillname" class="small">
<input type="text" required ng-model="skillname[1]" ng-init="skillname[1]=skills[1].skillname" class="small">

Parameterizing the data model in an Angular directive

I am implementing a control/widget that has three options, only one of which may be selected, which led me to using radiobuttons. This widget has to appear several times on various forms so I embarked on creating (incrementally) a dedicated directive.
The template of the directive is as follows:
<div class="row">
<span class="fieldlabel col-xs-3">{{title}}</span>
<div>
<label>
<input type="radio" data-ng-model="modelName" value="{{value1}}">
{{label1}}
</label>
<label>
<input type="radio" data-ng-model="modelName" value="{{value2}}">
{{label2}}
</label>
<label>
<input type="radio" data-ng-model="modelName" value="{{value3}}">
{{label3}}
</label>
</div>
The title, labels and values are correctly defined/computed either through using the custom directives or in the controller.
The last question I am facing now is how to specify different model bindings for each such widget? All instances of this widget currently share their model binding, which is of course not what I need. For instance, both of the divs in the fictitious example below would bind to "modelName" but I need them to bind to say "annotationsPos" and "menuPos" in the view's controller.
<div my-3option-radiobutton title="Show annotations"></div>
<div my-3option-radiobutton title="Menu position"></div>
How can I specify bindings in a custom directive?
EDIT 1
I think either I haven't really made myself clear or I lack some elements that would have helped me understand the answers that were offered.
If I had written the HTML by hand, I would have had something like this:
<div class="row">
<span class="fieldlabel col-xs-3">Position of your annotations</span>
<div>
<label>
<input type="radio" data-ng-model="annotationsPos" value="left">
Left of the element
</label>
<label>
<input type="radio" data-ng-model="annotationsPos" value="middle">
Through the element
</label>
<label>
<input type="radio" data-ng-model="annotationsPos" value="right">
Right of the element
</label>
</div>
<!-- -->
<div class="row">
<span class="fieldlabel col-xs-3">Position of the top menu</span>
<div>
<label>
<input type="radio" data-ng-model="menuPos" value="left">
Top left
</label>
<label>
<input type="radio" data-ng-model="menuPos" value="middle">
Top middle
</label>
<label>
<input type="radio" data-ng-model="menuPos" value="right">
Top right
</label>
</div>
<!-- -->
<div class="row">
<span class="fieldlabel col-xs-3">Position of notifications</span>
<div>
<label>
<input type="radio" data-ng-model="notificationPos" value="left">
Bottom left
</label>
<label>
<input type="radio" data-ng-model="notificationPos" value="middle">
Bottom middle
</label>
<label>
<input type="radio" data-ng-model="notificationPos" value="right">
Bottom right
</label>
</div>
Instead of copying and pasting this boilerplate code multiple times, I'm looking to do this thanks to an attribute directive:
<div my-3option-radiobutton title="Position of your annotations"></div>
<div my-3option-radiobutton title="Position of system notifications"></div>
<div my-3option-radiobutton title="Position of the top menu"></div>
What changes between these block is made of titles, values and, most importantly, model attribute values. I've covered the titles and values in the directive's controller in a non elegant way (see plunk further below). My problem is that I can't seem to:
determine where to specify an ng-model AND
have the "generated" HTML code refer correctly to that model attribute value (i.e. 'annotationPos', 'notificationsPos' and 'menuPos') AND
have two-way binding with the parent controller
EDIT 2
This plunk shows that #Suresh's answer is working, with a minor modification concerning the field name. However, the directive that I have written does not work (all widgets on the page bind to the same value), maybe due to it being an attribute directive and not an element directive. I don't want to have the latter type as it doesn't make sense to me and to top it all, this is to be integrated in an existing larger project, with other developers on it, that uses no element directive. This however does not mean that element directives are never to be used on the project.
Anyway, I'll keep looking for a solution. Thanks.
EDIT 3
I have resorted to using an ng-repeat directive in the template, just like #Suresh did. Using a developed template (i.e. repeating the input tag manually) does not work but I don't know whether that has to do with using/not using ng-repeat or rather with the way I "build" the values and labels in the controller.
Lessons learned from my plunk: even with a two-way binding over ngModel (below) in the controller of the widget:
all controls on the page will bind to the same value/variable unless ng-repeat is used
the parent controller's bound model is not updated if the template has data-ng-model="ngModel" instead of data-ng-model="$parent.ngModel"
scope: {
ngModel: "="
}
'use strict';
var app = angular.module('MyApp',[]);
app.directive("myRadiobutton", function () {
var templateHtml = function () {
return '<div class="form-group" >' +
'<label style="margin-right: 10px"; ng-repeat="(key, option) in options.valueList">' +
'<input type="radio" name="myfield" ng-value="option.value" ng-model="$parent.ngModel" ng-required="options.required" />{{option.title}}' +
'</label>' +
'</div>';
};
return {
scope: { options: '=', ngModel: '=' },
required: ['ngModel'],
restrict: 'E',
template: templateHtml,
};
});
app.controller('myController', function ($scope) {
$scope.radioGender = {
"label": "Gender",
"required": true,
"className": "",
"valueList": [
{
"title": "Male",
"value": "1"
},
{
"title": "Female",
"value": "2"
},
{
"title": "Others",
"value": "3"
}
]
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="myController" >
<my-radiobutton options="radioGender" ng-model="genderValue"></my-radiobutton>
<span>{{genderValue}}</span>
</div>
You should use the "=" in the directive scope to bind an object:
directives.directive("dirname", function () {
return {
restrict: 'A',
replace: false,
scope: {
model: '=', // pass a referecne object
title: '#' // path as string
},
controller: function ($scope, $rootScope) {
...
},
}
});
<div dirname title="Menu Position" model="modelName" ></div>

Easy way to access $route from html side in AngularJS

Is there a way to access $route from the html side of AngularJS?
I'd really like to do something like this in HTML and eliminate yet another $scope function. (yes, i know its not really something that works):
<button ng-disabled="!route.current.scope.currentform.$valid">
I am working on a large angular application that is somewhat 'down the road' in the development cycle. At this time we have decided to implement form validation (don't ask why it wasn't 1 or 2 on the list).
I am currently using two buttons in the footer "previous" and "next", both which need to ng-disabled set to !$scope.formname.$valid. This needs to work across multiple controllers / pages, since the buttons are in my footer on index.html.
The code i'm using right now looks like this:
// Want to eliminate this isFormValid() method
$scope.isFormValid = function() {
if ($route.current) {
if ($route.current.scope) {
return !$route.current.scope.currentform.$valid;
}
}
};
$scope.prevPage = function() {
if (angular.isFunction($route.current.scope.PrevAction)) {
// do stuff particular to the current controller
$route.current.scope.PrevAction()
}
};
$scope.nextPage = function() {
if (angular.isFunction($route.current.scope.NextAction)) {
// do stuff particular to the current controller
$route.current.scope.NextAction()
}
};
and the corresponding button code is as follows:
<div class="footer">
<button id="main-prev-button" type="button" class="btn btn-primary previous" ng-click="prevPage($event)" ng-disabled="isFormValid()">Previous</button>
<button id="main-next-button" type="button" class="btn btn-primary next" ng-click="nextPage($event)" ng-disabled="isFormValid()">Next</button>
</div>
Each page code looks something like this
<ng-form id="currentform" name="currentform">
<label>Full Name</label>
<input type="text" class="form-control" ng-model="nl.firstName" id="firstName" placeholder="First Name" name="firstName" ng-minlength="5" ng-maxlength="20" ng-required="true">
<pre>currentform.firstName.$error = {{ currentform.firstName.$error | json }}</pre>
<ng-messages for="currentform.firstName.$error">
<ng-message when="required">You did not enter a field</ng-message>
<ng-message when="minlength">Your field is too short</ng-message>
<ng-message when="maxlength">Your field is too long</ng-message>
</ng-messages>
</ng-form>
Add the $route to the root scope $rootScope and then access it in your html/view using the $rootScope.$route.
E.g:
angular.module('myApp').run(['$rootScope', '$route',
function ($rootScope, $route) {
$rootScope.$route = $route;
}]);

Pass ng-model attribute to custom directive

So, I have a form, where I need to use a custom directive.
What i need: pass the user model to the directive.
<form>
<input type="text" ng-model="user.login">
<input type="password" ng-model="user.password">
<span ng-custom-directive ng-model="user.testfield"></span>
</form>
Directive template looks like this:
<span><input type="checkbox" ng-model="[HERE I NEED user.testfield TO WORK WITH user]"> </span>
How I can pass the user model to directive template?
After form submit I need user.testfield to be avaliable in the $scope.user like:
console.log($scope.user)
{
login: 'test',
password: 'test',
testfield: true|false
}
You can solve it in the other way plunker
In brief:
scope: {
bindedModel: "=ngModel"
},
template: '<input type="text" ng-model="bindedModel">'
Well, I found similar question and resolved my problem in this way:
angular.module("myApp")
.directive "ngCustomDirective", () ->
restrict: 'A',
scope:
field: '#',
model: '='
template: '<span><input type="checkbox" ng-model="model[field]"></span>'
And directive usage will be:
<span ng-custom-directive
ng-bind-model="user"
ng-bind-field="testfield">
</span>

Categories