Bind a new ngModel on an existing element - angularjs-directive

Suppose I have something that looks like this:
<input ng-model="object.properties.property_name" options="{ updateOn: 'blur' }" ng-change="object.save('property_name')">
I would like that to instead be really short like this:
<input name="property_name" autosave="object">
But to do that I need to dynamically bind an ngModel to the existing input. I've shortened it up so far to look like this:
<input ng-model="object.properties.property_name" name="property_name" autosave="object">
And this is the directive that gets me that far:
.directive('autosave', [function() {
return {
restrict: 'A',
scope : {
autosave : '=',
},
link: function(scope, element, attrs) {
element.bind('change', function(){
scope.autosave.save(attrs.name);
});
}
}
}])
How do I dynamically add scope.autosave.properties[attrs.name] to the ngModel and bind it to the input tag?

I haven't found a way to create a model, which was the original question, but I have made it shorter by pulling the property name from the model like this:
<input ng-model="object.properties.property_name" autosave="object">
and this directive
.directive('autosave', [function() {
return {
restrict: 'A',
scope : {
autosave : '=',
},
link: function(scope, element, attrs) {
element.bind('change', function(){
var property_name = attrs.ngModel.match(/(?=[^.]*$)(\w+)/)[1];
scope.autosave.save(property_name);
});
}
}
}])
It's not ideal, but it's better.

Related

AngularJS: Setting ng-readonly variable to true inside directive does not work

I have created a custom directive for inputs.
Inside the HTML it is used like this:
<my-input class="input-text"
type="number"
ng-model="modelVariable"
ng-readonly="false" />
Now inside the directive myInput.js I have something like this:
var myInputDirective = (function() {
directives.directive('myInput', [function() {
return {
restrict: 'E',
transclude: 'element',
replace: true,
require: 'ngModel',
templateUrl: 'app/templates/common/myInput.tpl.html',
scope: {
ngModel: '=',
ngReadonly: '='
},
link: function($scope, $element, $attr, ngModelController) {
$scope.$watch('ngModel', function(newValue, oldValue) {
if (newValue != oldValue && newValue !== undefined && newValue !== null) {
$scope.ngReadonly = isReadOnly($attr.ngModel);
}
});
However, although this sets $scope.ngReadonly to true - the field is still not readonly - I am very confused as to why not.
You have to add in the view of your directive's tempalte set ng-readonly equals to the parent scope as one of the attributes of your input element.
Just like this:
return {
//..the other properties
scope: {
ngModel: '=',
ngReadonly: '=readOnly'
}
Then in the myInput.tpl.html find your input element and add this:
<input ng-readonly="readOnly">

angular directives not working with my directive

i created a directive called bootstrap-switch and put it as attribute to and input tag.
now, when i am trying to add some angular directives (like: ng-show, ng-disabled) it isn't working!
the html:
<input ng-show="false" type="checkbox" name="my-checkbox" bootstrap-switch>
the js:
sharedSwitch.directive('bootstrapSwitch', ["regService",
function(regService) {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
if (Number(attrs.ioStatus))
{
$(element).bootstrapSwitch('state',true,true);
}
else
{
$(element).bootstrapSwitch('state',false,true);
}
$(element).bootstrapSwitch('onSwitchChange',function(event,state){
flagNumber = Number(scope.baseFlag) + Number(scope.$index);
regService.fSave('F',flagNumber ,state);
});
}
};
}
]);
the input is showed any way.. and that because of the "bootstrap-switch" and i dont know why or how to fix it...
thank you for your help!

Unable to access $scope model

In my main html, I have a view which loads templates.
<div data-ng-view></div>
It loads a html whenever the link is clicked.
app.config(["$routeProvider", function ($routeProvider) {
'use strict';
$routeProvider
.when("/", {
templateUrl: "events.html"
});
}]);
On this page (template) , I have a directive which loads another html file
app.directive('ngPost', function () {
'use strict';
return {
restrict: 'A',
templateUrl: 'postbox.html'
};
});
I then use this directive on my events.html page by using <div data-ng-Post></div>
In postbox, I have two input fields and a button
<input type="text" id="user" data-ng-model="username" />
<input type="text" id="mess" data-ng-model="message"/>
<button data-ng-click="Add(eventid-1, username, message)">Post</button>
Upon clicking the button, I have some operations, then I try to clear the input fields, but I cannot. Method here :
$scope.Add = function (index, uname, msg) {
var a = {user: uname, message: msg, time: new Date()};
$scope.data[index].messages.push(a);
$scope.message = ''; // clearing here
$scope.username ='';
};
The clearing does not happen, I do not know why. My controller that has this Add method wraps the <div data-ng-view></div>in the main html file so it is the outermost controller and should have access to all $scope models inside. Why does it not work?
Note that the operations before the clearing works with no problems
Your add method is in the parent scope. The parent's scope cannot see it's children, it works the other way around. The message and username properties are defined in the directive's child scope. From a child you can reference parent properties, but not the other way around.
If you add scope: false and transclude: false to your directive, it won't create it's own scope and instead use its parent's scope, so your directive would look something like this:
angular.module('app', []).controller("myController",myController);
function myController($scope){
var ctrl = this;
ctrl.hello ="Hello"
};
angular.module('app').directive("childThing", function() {
return {
template: '<div>{{message}}</div><div>{{username}}</div>',
scope: false,
transclude: false,
controller: function($scope) {
$scope.username="Mike Feltman"
$scope.message="Hi Mike"
}
}
})
and you can access the elements that the directive adds to the scope from the parent like this:
<div ng-controller="myController as ctrl">
{{username}} in the parent.
<div>{{ctrl.hello}}</div>
<child-thing></child-thing>
</div>
Update using your template:
{{username}} in the parent.
{{ctrl.hello}}
Javascript:
function myController($scope){
var ctrl = this;
ctrl.hello ="Hello"
$scope.add = function() {
alert($scope.username)
}
};
angular.module('app').directive("childThing", function() {
return {
template: '<input type="text" id="user" data-ng-model="username" /><input type="text" id="mess" data-ng-model="message"/>',
scope: false,
transclude: false,
}
})

getting a attribute value in a directive in angular js

I have a directive
configModule.directive('iMemoryDiv',function () {
return {
restrict: 'E',
scope: {},
replace: true,
templateUrl: '/memoryDiv.html',
link: function(scope, el, attrs) {
scope.iObj = scope.$parent[attrs.bIObject];
if(angular.isDefined(scope.iObj)){
getSummary(scope.iObj);
}
function getSummary(iObj){
}
}
};
}
);
and I am calling it from html by passing an object attribute
<i-memory-div b-i-object="app"></i-memory-div> //here app is declared in a respective controller
but, here directive is getting loaded before the controller where value for 'app' is getting assigned.So scope.iObj = scope.$parent[attrs.bIObject] is always giving me undefined.
How can make directive load after the app value is getting assigned ??
Isn't it easier doing it this way:
configModule.directive('iMemoryDiv',function () {
return {
restrict: 'E',
scope: {
'bIObject': '=',
'getParentObject': '&'
},
replace: true,
templateUrl: '/memoryDiv.html',
link: function(scope, el, attrs) {
scope.$watch('bIObject', function(newValue) {
if(angular.isDefined(newValue)) {
getSummary(scope.getParentObject({name: newValue}));
}
});
function getSummary(iObj){
}
}
};
}
Or do you need more from the parent scope??
You could give your directive an isolated scope and add bIObject as a scope property:
scope: {
bIObject: '=',
},
you could then put your code in the controller: rather than link:
Use $observe service in link function,like this
if(attrs. bIObject) {
attrs.$observe('bIObject', function(value) {
console.log(value);
})
}

Directive Angularjs, $watch and HTML Element

I have a question. I coded a HTML page with an angularjs directive
this is my code
<menufileupload visible="rightVisible" alignment="right">
<input type="text" ng-model='expr'/>
<!--Other stuff-->
</menufileupload>
I'm trying to use a $watch to check any change in the text field
$scope.$watch(...) only worked when the text field is out of the
directive. So I think that I have to create a $watch in the directive. I did that. But it didn't work.
Directive
app.directive("menufileupload", function() {
return {
restrict: "E",
template: "<div>bla</div>",
link: function(scope, elem, attrs) {
scope.$watch('expr', function(obj) {
alert("it changed");
}, true);
}
};
});
Thanks for your help
You have a template in your directive, so everything inside it will be replaced by that <div> tag.
It works fine if you remove the template. I made a codepen example of your code:
http://codepen.io/argelius/pen/jEzQVJ
angular.module('test', [])
.directive('menufileupload', function () {
return {
restrict: "E",
link: function(scope, elem, attrs) {
scope.$watch('expr', function(obj) {
alert("it changed");
}, true);
}
};
});
If you want your input element to be shown inside the directive DOM you should use the ngTransclude directive. Please check it out in the documentation.