AngularJS directive input fields are read only - angularjs

So I am trying to write an AngularJS (version 1.4.8) directive which outputs the value of an attribute in an input box. The directive looks like this:
(function () {
angular.module("myApp").directive("inputAttributeDirective", function () {
return {
templateUrl: "inputAttributeTemplate.html",
// The directive is a custom element
restrict: "E",
// Declare the scope of the directive as isolated
scope: {
attribute: "=",
value: "="
}
}
});
})();
And the HTML template for it is the following (that's in inputAttributeTemplate.html):
<label id="AttributeLabel" class="control-label">{{ attribute }}</label>
<div id="AttributeInput" class="input-prepend">
<input id="attributeName" name="attributeName" class="form-control input-large" type="text" ng-model="value"/>
</div>
Where 'attribute' and 'value' correspond to the attributes of an object called 'Client' that looks like this:
vm.client = {
name: "Some Client",
description: "This is a cool client",
networkPath: "network path 1",
active: "1"
}
So basically, a Client has four attributes, name, description, networkPath, and active.
And the way I am including the directive is like so (this is in the parent HTML file):
<div class="panel-body">
<div ng-repeat="(key, value) in vm.client">
<incident-attribute-directive attribute="key" value="value"></incident-attribute-directive>
</div>
</div>
The problem is that, while it does display the attributes correctly, they are read-only and I need them to be editable:
Any idea why they are read-only?

Related

Not getting value of input type created through a directive in angularJS

I am using a directive for datepicker in angularJS. Below is my code for directive. It works well and it shows datepicker when I add datepicker directive in a div.now I want to use the selected date in my form when I send data in controller and from controller I pass it to php api via $http. when I check the data in php it does not shows date. Rest all fields are working fine only the field created with directive is not showing the values. I am pasting the code for directive, controller and form.
app.directive('datepicker', function () {
return {
restrict: 'A',
controller: 'datepickerCtrl',
controllerAs: 'dp',
template: '<div id="dob" ng-controller="profileCtrl" dob class="input-group date" data-date-format="dd-mm-yyyy" ><input class="from_to_input" name="1" type="text" ng-class={inputError:dp.validDate==false} class="form-control input-sm" ng-model="profile.value"/><span class="input-group-addon"><span class="glyphicon glyphicon-th"></span></span></div>',
scope: {
'value': '='
},
link: function (scope, element, attribute) {
// scope.variable = attribute.value;
}
};
});
below is code for controller for datepicker
app.controller('datepickerCtrl', function ($scope) {
$('.date').datepicker({ autoclose: true, todayHighlight: true });
var inputDate = new Date(moment($scope.value));
$scope.value = moment(inputDate).format("DD-MM-YYYY");
$('.date').datepicker('setDate', inputDate);
$scope.$watch('value', function (newVal) {
});
});
below is code for my form page
<form name="profileForm" role="form">
<input type="email" id="" name="email" ng-model="profile.email" class="login_input_area" placeholder="Email">
<div class="col-xs-2" datepicker value="date2"></div>
<input type="submit" value="Submit" ng-click="saveprofile(profile)" class="submit_btn">
</form>
My friend!
Please check online video tutorials for Angular directives!
Anyway, in your directive you have ng-model="profile.value", that should be "value" I think
second: remove ng-controller="profileCtrl" from directive template. Directives has they own local scope.
third: I prefer using restrict: "E", so you can use as custom tag, more readable on my opinion
one more: scope: { value : "="} , not scope: { 'value' : "="}
couple more: remove both controller: 'datepickerCtrl', controllerAs: 'dp'

Create custom input directive in angular

I would like to create a custom input that looks like that:
<my-input ng-model="aScopeProperty" placeholder="Enter text"
data-title="This is my title"></my-input>
my-input should receive any property that regular input can get (like placeholder and etc...).
the output should be like this (myInputTemplate.html):
<div class="my-input">
{{title}}
<input type="text" ng-model="text" />
</div>
I created a directive:
myApp.directive('myInput', function(){
return {
restrict: 'E',
require: 'ngModel',
templateUrl: '/myInput/myInputTemplate.html',
replace: true,
scope: {
text: '=ngModel',
title: '=title'
},
}
});
the ng-model is bindded ok now,
my question is:
How can I pass the attributes (like placeholder and etc) from my-input to the inside input?
I think that I approached it the wrong way, maybe I need to do it like that:
<input my-input ng-model="aScopeProperty" placeholder="Enter text"
data-title="This is my title"></input>
and to wrap the input with:
<div class="my-input">
{{title}}
<here will be the original input>
</div>
directive call should be like
<my-input ng-model="aScopeProperty" placeholder="'Enter text'" title="'This is my title'"></my-input>
note the placeholder="'Enter text'" Enter text with in quotes ('), this indicate these values are string so angular will not search for scope variable.
and in the directive
myApp.directive('myInput', function(){
return {
restrict: 'E',
require: 'ngModel',
templateUrl: '/myInput/myInputTemplate.html',
replace: true,
scope: {
text: '=ngModel',
title: '=title',
placeholder : '=placeholder'
},
}
});
and the template
<div class="my-input">
{{title}}
<input type="text" ng-model="text" placeholder="{{ placeholder }}" />
</div>
here is the demo Plunker
You can use ng-attr as the following:
<input type="text" ng-model="text" ng-attr-placeholder="{{placeholder}}"/>
And send placeholder as attribute in your scope as the following:
scope: {
text: '=ngModel',
title: '=title',
placeholder : '=placeholder'
}
I recommend to read about ng-attr-attrName, and this useful answer.
Dynamic attributes
Read my question, and accepted answer.
The second approach succeeded!
final code:
<input my-input ng-model="aScopeProperty" placeholder="Enter text"
data-title="This is my title">
The Directive:
app.directive('myInput', function () {
return {
restrict: 'A',
scope: {
title: '=title'
},
link: function ($scope, $element) {
var wrap = angular.element('<div class="my-input-wrapper" />');
$element.addClass('form-control').removeAttr('my-input');
wrap.insertBefore($element);
$element.appendTo(wrap);
if ($scope.title) {
var title = angular.element('<span class="my-title">' + $scope.title + '</span>');
title.appendTo(wrap);
}
},
}
});
I even created my first Plunker for it, unfortunately, the Plunker don't works because it doesn't recognize: insertBefore and appendTo
http://plnkr.co/edit/XnFM75vOBg4ifHUQzGOt?p=preview

AngularJS ng-bind to specific array element

I have elements in my page that's being repeated as rendered by ASP.Net MVC. (I need to do this for SEO purposes.)
Within these elements, I would like to have a text field that's bound by AngularJS.
Is there a way to do this?
In the code below, for example, the 2nd and 3rd text fields don't even allow me to type anything. Is it because I have not properly initialized the array or is there something wrong with the syntax?
<input type="text" ng-model="sometext" />
<input type="text" ng-model="sometextArray1[1]" />
<input type="text" ng-model="sometextArray2[1].value" />
<h1>Hello #1 {{ sometext }}</h1>
<h1>Hello #2 {{ sometextArray1[1] }}</h1>
<h1>Hello #3 {{ sometextArray2[1].value }}</h1>
If you are trying to let angular implicitly instantiate the array for you, I don't think arrays are assignable. If you are using a controller to instantiate your array this should work. I have created a fiddle.
JsFiddle
Html:
<div ng-app="mod">
<section ng-controller="Ctrl">
<input type="text" ng-model="someArray[0].value" ng-change="logValue(0)" />
<input type="text" ng-model="someArray[1].value" ng-change="logValue(1)"/>
<input type="text" ng-model="someArray[2].value" ng-change="logValue(2)"/>
<input type="text" ng-model="someArray[3].value" ng-change="logValue(3)"/>
</section>
</div>
Code:
(function () {
var app = angular.module('mod', []);
app.controller('Ctrl', ['$scope', '$log', function ($scope, $log) {
$scope.someArray = [
{value: "value 0"},
{value: "value 1"},
{value: "value 2"},
{value: "value 3"}
];
$scope.logValue = function (index) {
$log.info($scope.someArray[index]);
}
}]);
})();
You can add a directive to your list and another for each item using isolate scope.
Here's a plunker
you will get all the content of your items by transcluding them into a directive template
transclude: true,
//
<div ng-transclude></div>
You can add to your isolate scope per item from the attribute like you just need a way to index each scope like scope="scope1" | scope="scope2"
<list-item name="First Item" scope="scope1" func="func1(scope)">
In your directive you use operators like - # String, = Model, & Function
// Directive
scope: {
'name': '#',
'iscope': '=scope',
'func': '&'
}
Then in the container controller you can create the a sort of indexed array of item scopes like you would with an ng-repeat.
$scope.addItem = function(scope) {
$scope.items.push(scope);
};

AngularJS: ngInclude with custom scope

I have an existing directive that creates a generic wizard that will be used across my current organization. This wizard will have steps, and each step has an HTML template to be loaded as the "body" of the wizard. Currently, I'm adding the template using ng-include. I don't know which attributes the wizard will have, as it will come from the wizard consumer.
Question: Is there a way to bind the template models (attributes) to a directive scope variable instead to the scope itself?
Currently I have:
Directive HTML
<div ng-include="/step1.html"></div>
Note: This step1.html is just an example. The actual HTML template will come as a configuration parameter from the wizard directive.
Step 1 HTML
<input type="text" ng-model="data.email"/>
This is "forcing" the wizard consumer to include "data" for each of his attributes.
As a result, I was looking for something like this:
Directive HTML
<div ng-include="/step1.html" ng-scope="scope.data"></div>
Step 1 HTML
<input type="text" ng-model="email"/>
I was able to solve the problem using ng-transclude. Now, the consumer does not need to send me the HTML code (step1.html). Right now, this goes directly into the wizard directive body, as follows:
Sample Controller
angular.module('sampleApp').controller('SampleCtrl', function ($scope) {
$scope.email;
});
Sample HTML
<div wizard>
<div step title="Step 1">
<input type="text" ng-model="email"/>
</div>
</div>
Directives with transclude option
angular.module('sampleApp').directive('wizard', function () {
return {
restrict: 'A',
transclude: true,
scope: {
},
template: '<div> Wizard <div class="stepsContainer" ng-transclude /></div>'
};
});
angular.module('sampleApp').directive('step', function () {
return {
require: "^wizard",
restrict: 'A',
transclude: true,
scope: {
title: '#'
},
template: '<div ng-show="current"> {{ title }} <div ng-transclude></div></div>'
};
});
I believe what you need is a directive, with which you can have isolate scope, just like taking parameter from outer scope.
Directive
yourModule.directive('step1', function() {
return {
scope: {
data: '=anyname'
},
templateUrl: "/step1.html"
};
})
Step1 HTML
<input type="text" ng-model="data.email"/>
Then when the consumer uses it:
<div step1 anyname="scope.consumerData"></div>
Then anyname can be named anything meaningful to the consumer, without knowing the template is actually using data.

Set angularjs input directive name as a variable

I'm trying to set an input like
<form name="myForm">
<input name="{{ name }}"/>
</form>
It works in the dom. I see
<input name="whatever_name_is_set_to"/>
However in my ngForm I have
$scope.myForm: {
{{ name }}: { }
}
Doh'
Why am I doing this? I'm trying to create a directive so that I can build my forms programmatically. Then I can do something like
<div my-field
name="credits"
field="course.credits"
field-options="courseOptions.credits"
title="Credits"
></div>
Plunker
Updated 2017-03-23:
For AngularJS >= 1.3 interpolation is now supported in input names.
Original answer from 2014-06-05 (correct for AngularJS <= 1.2):
I just answered a similar question yesterday about dynamically named form elements. In short, no, you can't use interpolation to dynamically name your form fields - the interpolation string will end up in the field name as you have seen.
In your case you're probably going to need to look into dynamically compiling the input HTML inside your myField directive.
Here is a simplified example using $compile to dynamically generate form elements: http://jsfiddle.net/Sly_cardinal/XKYJ3/
HTML:
<div ng-app="myApp">
<form name="myForm" ng-controller="myController">
<div my-field
name="courseName"
field="course.courseName"
title="Course Name"></div>
<div my-field
name="credits"
field="course.credits"
title="Credits"></div>
<!-- Show that the values are bound. -->
<pre>course: {{course | json:' '}}</pre>
<!-- Show that the field is being registered with the ngFormController. -->
<pre>myForm.credits.$dirty: {{myForm.credits.$dirty}}</pre>
</form>
</div>
JavaScript:
angular.module('myApp', [])
.controller('myController', ['$scope', function($scope){
$scope.course = {
credits: 100,
courseName: 'Programming 201'
};
}])
.directive('myField', ['$compile', '$parse', function($compile, $parse){
// In a real project you'd probably want to use '$templateCache' instead
// of having strings in your code.
var tmpl = $compile('<label>{{title}}</label>');
return {
scope: true,
link: function(scope, element, attr){
scope.title = attr.title;
var newEl = angular.element('<input type="text"/>');
newEl.attr('ng-model', attr.field);
newEl.attr('name', attr.name);
tmpl(scope, function(fieldEl, scope){
$compile(newEl[0].outerHTML)(scope, function(el, scope){
fieldEl.append(el);
element.append(fieldEl);
});
});
}
}
}]);
A note on this example:
This is a very specific situation - generating dynamic form elements - that requires the use of $compile. This is not the "go to" solution when working with Angular inputs and forms - Angular will handle all the common situations with directives, data-binding and everything else the framework provides. Plus, as Marc Kline's comment shows, it looks like at some point Angular will handle dynamic form management itself at some point in the future.
If you were to continue down the path using $compile to generate these form elements then you'd probably want to use the $templateCache to manage your templates so you're not trying to manage template strings inside your directive.
Old question, but in case someone is looking for a way to do what question asked, you can create a directive that will dynamically create the name of the element after $compile'ing it.
An updated version of the answer that #Sly_cardinal posted is here: http://jsfiddle.net/XKYJ3/1/
HTML
<div ng-app="myApp">
<form name="myForm" ng-controller="myController">
<label for="{{ course.courseName.name }}" ng-bind="course.courseName.title"></label>
<input id="{{ course.courseName.name }}" dynamic-input-name="course.courseName.name" ng-model="course.courseName.value" type="text" required />
<br />
<label for="{{ course.credits.name }}" ng-bind="course.credits.title"></label>
<input id="{{ course.credits.name }}" dynamic-input-name="course.credits.name" ng-model="course.credits.value" type="number" required />
<!-- Show that the values are bound. -->
<pre>course: {{course | json:' '}}</pre>
<!-- Show that the field is being registered with the ngFormController. -->
<pre>myForm.credits_field.$dirty: {{ myForm.credits_field.$dirty }}</pre>
</form>
</div>
Javascript
angular.module('myApp', [])
.controller('myController', ['$scope', function($scope){
$scope.course = {
credits: {
title: 'Credits',
value: 100,
name: 'credits_field'
},
courseName: {
title: 'Course name',
value: 'Programming 201',
name: 'course_name_field'
}
};
}])
.directive('dynamicInputName', ['$compile', '$parse', function($compile, $parse){
return {
restrict: 'A',
terminal: true,
priority: 100000,
link: function(scope, elem) {
var name = $parse(elem.attr('dynamic-input-name'))(scope);
elem.removeAttr('dynamic-input-name');
elem.attr('name', name);
$compile(elem)(scope);
}
};
}]);

Resources