Dynamically adding multiple custom directives associated with single controller in angularjs 1 - angularjs

I have an html template attached to a controller and three directives. There are three buttons. On clicking a button a directive is added to the page(using ng-click) like this(the following is in my controller not in directive):
$scope.addFilterDimension = function() {
console.log('CLICKED!!!!!!!')
var divElement = angular.element(document.querySelector('#filterDimension'));
var appendHtml = $compile('<filter-directive></filter-directive>')($scope);
divElement.append(appendHtml);
}
Similarly for other buttons, other directives are added. Now, I can keep adding as many of these directives as I like, which is the use case here.
These directives are basically like forms containing either dropdowns, input boxes or both. The values user selects from the dropdowns or enters in input boxes have to be sent back to the backend to be stored in the DB.
This is one of the directives(others are very similar):
anomalyApp.directive('filterDirective', function() {
return {
restrict: "E",
scope: {},
templateUrl:'filter-dimension.html',
controller: function($rootScope, $scope, $element) {
$scope.dimensionsOld = $rootScope.dimensionsOld;
$scope.dimensions = $rootScope.dimensions;
$scope.selectedDimensionName = $rootScope.selectedDimensionName;
$scope.selectedDimensionValue = $rootScope.selectedDimensionValue;
$scope.extend = $rootScope.extend;
$scope.selectedExtend = $rootScope.selectedExtend;
$scope.isDateField = $rootScope.isDateField;
console.log($scope.dimensions);
$scope.Delete = function(e) {
//remove element and also destoy the scope that element
$element.remove();
$scope.$destroy();
}
}
}
});
Now, in my controller I assign $rootscope to my values which have to be used in the directives and thus catch them in the directive. Example:
$rootScope.dimensions = temp.map(d=>d.dimName);
$rootScope.selectedDimensionName = '';
$rootScope.selectedDimensionValue = '';
And this is how I retrieve my values from added directives:
var retriveValue = function() {
var filtersData = [];
var constraintsData = [];
var variablesData = [];
var ChildHeads = [$scope.$$childHead];
var currentScope;
while (ChildHeads.length) {
currentScope = ChildHeads.shift();
while (currentScope) {
if (currentScope.dimensions !== undefined){
filtersData.push({
filterDimensionName: currentScope.selectedDimensionName,
filterDimensionValue: currentScope.selectedDimensionValue,
filterDimensionExtend: currentScope.selectedExtend,
filterDimensionIsDateFiled: currentScope.isDateField
});
}
if (currentScope.constraintDimensions !== undefined){
filtersData.push({
constraintDimensionName: currentScope.selectedConstraintName,
constraintDimensionValue: currentScope.selectedConstraintValue,
constraintDimensionExtend: currentScope.selectedConstraintExtend,
constraintDimensionVariable: currentScope.selectedConstraintVariable,
constraintDimensionOperator: currentScope.selectedOperator,
constraintDimensionVariableValue: currentScope.constraintVariableValue,
constraintDimensionIsDateField: currentScope.isDateFieldConstraint
});
}
if (currentScope.variableNames !== undefined){
console.log('currentScope.selectedVariableVariable',currentScope.selectedVariableVariable);
filtersData.push({
variableName: currentScope.selectedVariableVariable,
variableOperator: currentScope.selectedVariableOperator,
variableValue: currentScope.variableVariableValue,
variableExtend: currentScope.selectedVariableExtend
});
}
currentScope = currentScope.$$nextSibling;
}
}
return filtersData;
}
This is one of the directive's template:
<div >
<div>
<label>Dimension</label>
<select class = "custom-select custom-select-lg mb-6" ng-model="selectedDimensionName" ng-options="dimension for dimension in dimensions">
<!-- <option ng-repeat="table in tables track by $index">{{table}}</option> -->
</select>
</div>
<div>
<label>Date Field</label>
<input type="checkbox" ng-model="isDateField">
</div>
<div>
<label>Value</label>
<select multiple class = "custom-select custom-select-lg mb-6" ng-model="selectedDimensionValue" ng-options="val for val in ((dimensionsOld | filter:{'dimName':selectedDimensionName})[0].dimValues)"></select>
</span>
</div>
<div>
<label>Extend</label>
<select class = "custom-select custom-select-lg mb-6" ng-model="selectedExtend" ng-options="val for val in extend"></select>
</span>
</div>
<button type="button" class="btn btn-danger btn-lg" ng-click="Delete($event)">Delete</button>
This is in the main html to add the directive:
<div id="filterDimension"> </div>
I know this is not a good way, but please suggest a better one.
Secondly, a new change has to be made where inside one of the directives there will be a button, clicking on which 2 dropdowns(or simply one more directive) will be added and can be added as many times as needed(just like the other directive).
The issue here is this one is a directive inside another directive and I am facing unusual behavior like:
When I add the parent directive it is fine, I add the child directives its fine, but when I add a second parent and try to add its child, they get appended inside the first directive.
Even if I somehow manage to get out of the above point I do not know how to retrieve values from such directives.
PS: I am new in AngularJS or front-end for that matter, the retriveValue() and using rootscope I got from somewhere online.

Related

Adding Text Box to Angular Form

I'm trying to dynamically add a textbox to an Angular form. I'm using ng-repeat and I can add the text box easily by just pushing, an empty string to the array. It will add another textbox, the problem is that ng-model is not syncing the data, and when text is added it remains an empty string.
The text boxes that get created from the initial array sync just fine, it's just the newly added text boxes that are not working.
I've been looking around and one suggestion I saw was to always use a "." when using ng-model, but that didn't work for me.
<div ng-repeat="text in image.text track by $index">
<md-input-container class="md-block" novalidate>
<label>Text {{$index + 1}}:</label>
<textarea md-maxlength="2500" md-midlength="1" required md-no-asterisk name="text"
placeholder="{{text}}"
ng-model="text"></textarea>
</md-input-container>
</div>
Controller:
(function () {
'use strict';
angular
.module('app.article')
.controller('ArticleEditController', ArticleEditController);
ArticleEditController.$inject= ['articleEditDataService', '$routeParams', '$log'];
function ArticleEditController(articleEditDataService, $routeParams, $log) {
var vm = this;
 var site = $routeParams.site;
var articleName = $routeParams.article;
var articleRevision_id = $routeParams.revision_id;
vm.data = {};
vm.addNewText = addNewText;
vm.removeText = removeText;
vm.saveArticle = saveArticle;
vm.numMessage = 1;
activate();
function activate(){
getArticle();
}
function getArticle(){
var data = articleEditDataService.getArticle(site, articleName, articleRevision_id);
data.then(function successResponse(res) {
vm.data = res.results.data;
}, function errorResponse (res) {
console.log(res);
});
}
function saveArticle(){
var article = articleEditDataService.postArticle(vm.data, site, articleName, articleRevision_id);
console.log(vm.data);
article
.then(updateArticleSuccess)
.catch(updateArticleError);
}
function updateArticleSuccess(message){
$log.info(message);
}
function updateArticleError(errorMessage){
$log.error(errorMessage);
}
function addNewText (index, key) {
vm.data.content.image_sets[key].text.push("");
}
function removeText (index, key) {
if(vm.data.content.image_sets[key].text.length > 1){
vm.data.content.image_sets[key].text.pop();
}
}
};
})();
initialize the model variable within your controller as an empty object.
then within your text input your ng-model="model[$index]".

angularjs: scope value doesn't get updated in view

there are buttons in detail.html file:
<div ng-controller="test.views.detail">
<div data-ng-repeat="item in details" scroll>
<button ng-click="showDetails(item)">The details</button>
in detail.js file
angular.module('test')
.controller('test.views.detail', function($scope) {
$scope.detailsClicked = false;
$scope.showDetails = function(item){
$scope.detailsClicked = true;
}....
in formDetail.html code:
<div ng-controller="test.views.detail">
{{detailsClicked}}
<div ng-if="detailsClicked">...
Initially it shows false for detailsClicked, when I click on button it goes to showDetails function but value of $scope.detailsClicked never get updated! It is straight forward not sure why it doesn't work:(
This is because you're using the same controller at two places and expecting the scope object to be the same which it is not. Everytime you call ng-controller in your markup a new scope object will be created. If you want them to be based off the same data then use a service.
Here is an example
app.controller('test.views.detail', function($scope, detailsClicked) {
$scope.detailsClicked = detailsClicked;
$scope.showDetails = function(item){
$scope.detailsClicked.isClicked = true;
}
});
Create a factory/service which will retain the data, make sure the data is a
app.factory('detailsClicked', function(){
var data = {
isClicked: false
}
return data;
});

Get value from HTML page to angularJS file

I tried to get the HTML page value to angularJS function , The below steps are which i tried.
HTML page :
<label class="item-input item-stacked-label">
<span class="input-label cont_det_label">First Name</span>
<p class="contact_display" id="txtFirstName" ng-model="testName">Satya</p>
</label>
angularJS Page :
.controller('SocialNetworkCtrl', ['$scope','$http','$state','ContactsService','$ionicNavBarDelegate','$ionicLoading','$ionicPopup',function($scope, $http, $state, ContactsService, $ionicNavBarDelegate, $ionicLoading,$ionicPopup) {
$scope.showUserProfile = function() {
$state.go("linkedin");
var firstname = (document.getElementById("txtFirstName").value);
}
}])
So I need var firstname = Satya ?? Is it correct way please guide me to access this value .
var firstName = $scope.testName
<input ng-model="testName" />
testName is the ng-model name that you have give. It will be automatically binded to your controller. No need the get the value using document.getElementById
Wrong usage , why ng-model in <p> tag??
Update
Change your fiddle with the following code, it will work. Also make sure framework is selected properly (as in the image)
<div ng-app ng-controller="testController">
<input ng-model="testDataName" ng-change="check()" /> {{testDataName}}
After ng-change : {{checkName}}
</div>
function testController($scope) {
$scope.testDataName="Dummy Name";
$scope.check = function () {
$scope.checkName=$scope.testDataName;
console.log($scope.checkName);
};
}
its a text node, you will require .innerHTML or '.innerText', .value is for form inputs
var firstname = (document.getElementById("txtFirstName").innerHTML);
and don't use ng-model on a p element, change it to like this
<p class="contact_display" id="txtFirstName">{{testName}}</p>
just use $scope.testName to get the value, no need for firstname = (document.getElementById("txtFirstName").innerHTML); querying DOM for value is jQuery style, use angular the $scope for 2 way bindings
Read more at official doc
Update here is updated function on loginCtrl
.controller('loginCtrl', ['$scope', function ($scope) {
$scope.testNameData = 'Satya';
$scope.doLogin = function() {
alert($scope.testNameData);
};
}])
If you really want to go jQuery way here is what you can do, its not recommended, you should use angular directive to do DOM manipulation
$scope.showUserPro = function() {
$ionicLoading.show();
// Here i need the value of <p tag>
var name = document.getElementById("txtFirstName"),
firstNameFromHtmlPtag = name.innerText;
console.log(firstNameFromHtmlPtag, 'Doing API Call 1');
}

AngularJS - Why is the DOM not updating

So, what I am trying to do is add a "stack" - a basic JS object - into an array called cardStacks, declared the stackCtrl function. I've read that the controller in AngularJS is not supposed to do very much, nor do you want the manipulation of DOM elements done the controller. So what I have done is create a "save" directive which, as you could probably guess, adds a new "stack" into $scope.cardStacks (if the object is not being edited)
Everything seems to work ok, up until when the template is supposed to update. console.log() reveals that objects are going into an array, but because the template is not being updated, I can only guess it is not $scope.cardStacks.
Can somebody give the following a look and tell me why the template is not listing the "stacks" in the template?
Consider the following:
Template:
<div ng-controller="stackCtrl">
<stackeditor></stackeditor>
<p>Stacks:</p>
<ul class="stack-list" ng-repeat="stack in cardStacks">
<li>{{stack.title}}
<span class="stack-opts"> (Edit | Delete)</span>
</li>
</ul>
</div>
Template for the stackeditor tag/directive:
<div>
<span class="button" ng-click="show=!show">+ New</span>
<form ng-show="show" novalidate>
<input type="text" ng-model="stackTitle" required/>
<button class="button" save>Save</button>
<button class="button" reset>Cancel</button>
<div ng-show="error"><p>{{error}}</p></div>
</form>
</div>
Controller:
function stackCtrl($scope) {
$scope.cardStacks = [];
$scope.stackTitle = "";
$scope.addStack = function(title) {
var newStack = {};
newStack.title = title;
$scope.cardStacks.push(newStack);
}
$scope.removeStack = function($index) {
console.log("removing Stack...");
}
$scope.editStack = function(element) {
console.log("editing Stack...");
}
}
Directive:
fcApp.directive('save', function() {
var linkFn = function($scope, element, attrs) {
element.bind("click", function() {
if (typeof $scope.stackTitle !== 'undefined' && $scope.stackTitle.length > 0) {
if ($scope.edit) {
$scope.editStack(element);
} else {
$scope.addStack($scope.stackTitle);
}
} else {
$scope.error = "Your card stack needs a title!";
}
});
});
return {
restrict: "A",
link: linkFn
}
}
});
Try using $apply:
$scope.cardStacks.push(newStack);
$scope.$apply(function(){
$scope.cardStacks;
}
Rendering might be the problem..... Hope it helps.
The save function would be better in stackCtrl and then use ng-click in the template to call it.
You are right that manipulating the DOM in the controller is bad practice, but you are just updating an object in the controller - angular is sorting out the DOM which is fine.

How do I use a directive to toggle a slide animation on an element from my controller?

I am confused on the following scenario. Let's say I have a table with rows. When a user clicks a button in the table I want a user form to slide down with jQuery and display the form with the selected row values. Here is what I am currently doing that doesn't quite make sense:
View
<tr ng-click="setItemToEdit(item)" slide-down-form>
...
<form>
<input type="test" ng-model={{itemToEdit.Property1}} >
<button ng-click=saveEditedItem(item)" slide-up-form>
<form>
Control
$scope.itemToEdit = {};
$scope.setItemToEdit = function(item) {
$scope.itemToEdit = item;
});
$scope.saveEditedItem = function(item) {
myService.add(item);
$scope.itemToEdit = {};
}
Directive - Slide-Up / Slide-Down
var linker = function(scope, element, attrs) {
$(form).slideUp(); //or slide down
}
It seems the my directive and my control logic are too disconnected. For example, what happens if there is a save error? The form is already hidden because the slideUp event is complete. I'd most likely want to prevent the slideUp operation in that case.
I've only used AngularJS for about a week so I'm sure there is something I'm missing.
Sure, it's a common problem... here's one way to solve this: Basically use a boolean with a $watch in a directive to trigger the toggling of your form. Outside of that you'd just set a variable on your form to the object you want to edit.
Here's the general idea in some psuedo-code:
//create a directive to toggle an element with a slide effect.
app.directive('showSlide', function() {
return {
//restrict it's use to attribute only.
restrict: 'A',
//set up the directive.
link: function(scope, elem, attr) {
//get the field to watch from the directive attribute.
var watchField = attr.showSlide;
//set up the watch to toggle the element.
scope.$watch(attr.showSlide, function(v) {
if(v && !elem.is(':visible')) {
elem.slideDown();
}else {
elem.slideUp();
}
});
}
}
});
app.controller('MainCtrl', function($scope) {
$scope.showForm = false;
$scope.itemToEdit = null;
$scope.editItem = function(item) {
$scope.itemToEdit = item;
$scope.showForm = true;
};
});
markup
<form show-slide="showForm" name="myForm" ng-submit="saveItem()">
<input type="text" ng-model="itemToEdit.name" />
<input type="submit"/>
</form>
<ul>
<li ng-repeat="item in items">
{{item.name}}
<a ng-click="editItem(item)">edit</a>
</li>
</ul>

Resources