I have an AngularJS directive, which needs to be appended after an HTML element which called it. The elements structure can have many nested buttons on different levels of DOM structure.
Right now a directive gets appended in the wrong place instead of a container element that contains the button, which called the append function.
It looks like this:
The text should be appended after a button, which was clicked.
Directive:
app.directive('recursiveFields', function ($compile, $http) {
return {
scope: {
field: '=field',
model: '=model'
},
restrict: 'E',
replace: true,
controller: "httpPostController",
template: '<div ng-repeat="nestedField in field.nestedFields"><div ng-show="{{!nestedField.isEntity && !nestedField.isEnum}}">' + '<p ng-show={{nestedField.isRequired}}>{{nestedField.name}}*: </p>' + '<p ng-show={{!nestedField.isRequired}}>{{nestedField.name}}: </p>' + '<input type="text" ng-model="model[nestedField.name]" ng-change="getCreateEntityAsText()"' + 'class="form-control" placeholder="{{parseClassName(nestedField.type)}}">' + '</div>' + '<div ng-show="{{nestedField.isEnum}}">' + '<p ng-show={{nestedField.isRequired}}>{{nestedField.name}}*: </p>' + '<p ng-show={{!nestedField.isRequired}}>{{nestedField.name}}: </p>' + '<select ng-model="model[nestedField.name]" ng-change="getCreateEntityAsText()" class="form-control">' + '<option></option>' + '<option ng-repeat="enumValue in nestedField.enumValues" label={{enumValue.name}}>{{enumValue.ordinal}}</option>' + '</select>' + '</div>' +
'<div ng-show="{{nestedField.restResourceName != null}}">' + '<accordion close-others="oneAtATime">' + '<accordion-group heading={{nestedField.name}} is-open="false">' + /*'<recursive-fields model="createEntityResource" field="field"></recursive-fields>'*/
'<button type="button" ng-click="appendDirective()">I should append a "recursiveFields" directive</button>' + '</accordion-group>' + '</accordion>' + '</div>' + '</div>',
link: function (scope, element, attrs) {
console.log("1");
if (scope.field.restResourceName != null) {
$http.get(CONSTANTS.EXPLAIN_URL + "/" + scope.field.restResourceName)
.success(function (data, status) {
scope.field.nestedFields = [];
data.content.resource.fields.forEach(function (field) {
if (field.isEnum) {
$http.get(CONSTANTS.ENUMS_URL + scope.$root.parseClassName(field.type)).success(function (data, status) {
field.enumValues = [];
for (var index in data.content.values) {
field.enumValues.push(data.content.values[index]);
}
})
}
scope.field.nestedFields.push(field);
})
})
}
scope.appendDirective = function () {
var recursiveFields = $("<p>Insert me</p>");
recursiveFields.insertAfter(element[0]);
$compile(recursiveFields)(scope);
}
}
}
})
Does anyone know how to solve this issue with Angular? Every useful answer is highly appreciated and evaluated.
Thank you.
ngClick has access to the $event that can be passed to your method like this:
<button type="button" ng-click="appendDirective($event)"
That event has a property target.
Check this: https://docs.angularjs.org/api/ng/directive/ngClick
and this: https://docs.angularjs.org/guide/expression#-event-
Related
I have the below code to bind html in my html page,
<div bind-html-compile="menuButtonView"></div>
I have the below code in my controller,
dashboardService.getTemplateMetaData(data.templateCategory)
.success(function(data) {
console.log(data);
$scope.buttonArray = data.btnArray;
$scope.firstMenuLabel = data.firstMenuLabel;
if (data.firstMenuLabel) {
$scope.menuButtonView = '<obl-menu-group label="{{firstMenuLabel}}" icon="fa-pencil-square-o">' +
'<div data-ng-repeat="btn in buttonArray" >' +
'<div ng-if="btn.menuTitle !=="Site Settings" ">' +
'<obl-menu-button label="{{btn.menuTitle}}" icon="fa fa-file-image-o" menu-function="{{btn.menuFunction}}">' +
'</obl-menu-button></div>' +
'<div ng-if="btn.menuTitle ==="Site Settings"">' +
'<obl-menu-group label="{{btn.menuTitle}}" icon="fa-pencil-square-o" class="md-sub-menu">' +
'<obl-menu-button label="Contact Us" icon="fa fa-file-image-o" click-title="contactUs"></obl-menu-button>' +
'</obl-menu-button></div>' +
'</div>' +
'<obl-menu-group>';
}
}).error(function(err) {
});
}).error(function(err) {
});
The ng-if inside the ng-repeat is not working. I can't see anything wrong in this code. Is there a problem with the quotation marks?
use single quote in ng-if
'<div data-ng-repeat="btn in buttonArray" >' +
'<div ng-if="btn.menuTitle !=='Site Settings'">'+
//Your code
</div>'
'<div ng-if="btn.menuTitle ==='Site Settings'">'+
//Your code
</div>'
'</div>'
I want to enable a button based on whether there's the atribute editable in the directive:
Editable:
<table-list items="users" editable></table-list>
Non-editable:
<table-list items="users"></table-list>
I tried this:
.directive('tableList', function ($attr) {
return {
restrict: 'EA',
template: '<a ng-if="editable" class="btn btn-default" href="#">Add to group</a>' +
'<table class="table table-stripped">' +
'<thead>' +
'<tr>' +
'<th>#</th>' +
'<th>Name</th>' +
'<th>Users</th>' +
'<th>Buildings</th>' +
'<th>Created at</th>' +
'</tr>' +
'</thead>' +
'<tbody>' +
'<tr ng-repeat="item in items">' +
'<th scope="row"><input type="checkbox" value="0"></th>' +
'<td>{{item.name}}</td>' +
'<td>_</td>' +
'<td>_</td>' +
'<td>_</td>' +
'</tr>' +
'</tbody>' +
'</table>',
scope: {
items: "=",
editable: "="
},
But the button isn't showing.
What's the proper way of doing this?
If you want to rely on a presence of the attribute only (even if it is set to false), you can check if it is defined with the $attrs in the link function:
scope: {
items: "="
},
link: function($scope, $element, $attrs) {
$scope.editable = $attrs.editable !== undefined;
}
JSFiddle.
However, this will not be reactive (i.e. if you add the attribute in runtime, directive won't show the button). You might use $attrs.$observe() if you need it.
Just add true:
<table-list items="users" editable="true"></table-list>
JSFiddle
When getting an attribute and setting it to the scope like that you're setting the value. And your editable-attribute has no value. If you'd do <table-list items="users" editable="true"></table-list> it would work better.
And in the directive you could probably use # instead of = for the attribute.
This will set the editable property on the scope to the string "true", if you want it to be a boolean you might need some logic in the link function
Using angularjs, I am trying to populate a form dynamically and then submit the forms data via POST to a server.
I created a data variable in my controller (to POST later)
$scope.data = {};
Then in my html, to create the elements in the form
<div ng-repeat="(name, attributes) in fields">
<element myVar="data.{{name}}" name="{{name}}" attributes="{{attributes}}" ></element>
</div>
Where fields looks like
{"agency":{"name_displayed":"Agency","size":"30","tag":"input","type":"text"},"department":{"name_displayed":"Department","size":"30","tag":"input","type":"text"},"description":{"cols":"50","name_displayed":"Description","rows":"4","tag":"textarea"}}
The element directive then looks like this, but is throwing errors
demoApp.directive("element", function() {
var template = function(name, attributes, results) {
var templateString = "<" + attributes.tag;
for (var attribute in attributes) {
if (attribute != "name_displayed" && attribute != "tag" && attribute != "options") {
templateString += " " + attribute + '="' + attributes[attribute] + '"';
}
}
if (attributes.tag == "input") {templateString += ' value="' + results + '"';}
templateString += ' name="' + name + '">';
templateString += ' ng-model="myVar">';
if (attributes.tag == "select") {
for (var i=0; i<attributes.options.length; i++) {
templateString += "<option value=" + attributes.options[i] + ((attributes.options[i] == results)? " selected" : "") + ">" + attributes.options[i] + "</option>";
}
}
if (attributes.tag == "textarea") {
templateString += results;
}
templateString += "</" + attributes.tag + ">";
var toReturn = attributes.name_displayed + ": " + templateString;
return toReturn;
};
return {
restrict: "E",
scope: {
myVar: '='
},
link: function(scope, element, attrs) {
var attributes = angular.fromJson(attrs.attributes);
var tpl = template(attrs.name, attributes, attrs.results);
element.html(tpl);
}
};
});
Without the myVar attribute and scope object in the directive, this works fine (to display the form). Am I missing something about the two-way data binding here? Or is there a better way to do this? - Thanks
It seems strange that you append HTML without compilation. I would change the link first of all:
....
link: function(scope, element, attrs) {
var attributes = angular.fromJson(attrs.attributes);
var tpl = template(attrs.name, attributes, attrs.results);
var tpl_compiled = angular.element($compile( tpl )(scope));
element.html(tpl_compiled);
}
...
By this way we tell to angular to do a digest cycle over new appended data. Maybe this a reason why with isolate scope the myVar didn't fired.
Hope it will help,
In your html myVar needs to be formatted like my-var. Do you really need an isolated scope on this directive? Look at this plunker and add in Maxim Shoustin example.
Plunker
I'm using latest version of AngularJS which is 1.2rc3 along with Bootstrap for styling and have directive like this:
angular.module('form.field', [])
.directive('efield', function() {
return {
restrict: 'E',
scope: {
form: '#',
fname: '#',
label: '#'
},
template: "<div data-ng-class=\"{{form}}.{{fname}}.$valid ? 'form-group' : 'form-group has-error'\">" +
"<label class='control-label' for='{{fname}}'>{{label}}</label>" +
"<input type='text' class='form-control' name='{{fname}}' data-ng-required='false' data-ng-model='Form.test'>" +
"</div>"
}
});
html snippet looks like this:
<form name="form" novalidate="novalidate">
<efield form="form" fname="test" label="field"></efield>
</form>
I'm using directive to wrap field html and angular's validation directives to reduce boilerplate code. Problem is even if data-ng-required='false' is set on the input field, parent div is getting 'form-group has error' class instead of just 'form-group'. What am I doing wrong here?
You are running into problems due to the isolated scope you created in your directive. There are several ways to approach it. Here is one that will remove the isolated scope and use template:function(elem,attrs) instead
.directive('efield', function () {
return {
restrict: 'E',
template: function (elem, attrs) {
return "<div data-ng-class=\"" + attrs.form + "." + attrs.fname + ".$valid ? 'form-group' : 'form-group has-error'\">" +
"<label class='control-label' for='" + attrs.fname + "'>" + attrs.label + "</label>" +
"<input type='text' class='form-control' name='" + attrs.fname + "' data-ng-required='false' data-ng-model='Form.test'>" +
"</div>"
}
});
Now scope will be that of the parent scope
Take a look at this directive https://github.com/nelsonomuto/angular-ui-form-validation
So I havea list of posts in a forum format, and right now I want to be able to access the posterID property of the store post when clicking on the avatar image of the user. I've attached a select listener to the image, but I'm unsure how to access the parent store of the image. I've tried looking through sencha's documentation and search has failed me.
store: 'topicStore',
itemTpl:
'<div class="post">' +
'<div class="header">' +
'<img id="avatar" src="{avatar}" width=48 height=48>' +
'<h2>{displayName}</h2>' +
'<div class="date">on {postDate:date("m/d/Y")}</div>' +
'</div>' +
'<div class="body">' +
'{message}' +
'</div>' +
'<tpl if="signature">' +
'<hr />' +
'<div class="signature">' +
'{signature}' +
'</div>' +
'</tpl>' +
'</div>',
listeners: {
select: function() { return false;},
tap: {
fn: function(event, el, record){
tempElement = el.src;
if (el.id != 'avatar'){
tempElement = tempElement.replace('_th.jpg', '_mid.jpg');
var logo = Ext.create( 'Ext.Img', {
src: tempElement,
id: 'logo',
mode: 'element'
});
app.fireEvent('forum-onimgview',logo);
}
else if (el.id == 'avatar'){
console.log(record);
}
},
element: 'element',
delegate: 'img'
}
}
Does anyone know how to get the parent store of the selected Item? All the gets returned on the listener is the image and dom elements. Thanks in advance!
Try using itemTap listener, it will give you reference to the list.
I use it like this:
listeners: {
itemtap: function (list, index, element, record)
{
/* Put your logic here*/
}
}
More details can be found here: http://docs.sencha.com/touch/2-1/#!/api/Ext.dataview.DataView-event-itemtap