How to dynamically append templates to a page in Angular - javascript

So the situation is as follows:
I have an input bar where a user can search up a business name or add a person's name (and button to select either choice). Upon hitting enter I want to append a unique instance of a template (with the information entered by the user added). I have 2 templates I've created depending of if the user is searching for a business or a person.
One approach I've thought about is creating an object with the data and adding it with ng-repeat, however I can't seem to get the data loaded, and even then don't know how I can store reference to a template in my collection.
The other idea I've come across is adding a custom directive. But even then I've yet to see an example where someone keeps appending a new instance of a template with different data.
Here is the code so far:
payments.js:
angular.module('payment-App.payments',['ngAutocomplete'])
.controller('paymentController', function($scope, $templateRequest/*, $cookieStore*/) {
$scope.businessID;
$scope.address;
$scope.isBusiness = false;
$scope.payees = [];
$scope.newPayee = function () {
this.businessID = $scope.businessID;
this.address = $scope.address;
}
$scope.submit = function () {
var text = document.getElementById("businessID").value.split(",");
$scope.businessID = text[0];
$scope.address = text.slice(1).join("");
$scope.newPayee();
}
$scope.addPayee = function () {
$scope.submit();
$scope.payees.push(new $scope.newPayee());
console.log($scope.payees);
}
$scope.selectBusiness = function () {
//turns on autocomplete;
$scope.isBusiness = true;
}
$scope.selectPerson = function () {
//turns off autocomplete
$scope.isBusiness = false;
}
$scope.fillAddress = function () {
// body...
}
})
.directive("topbar", function(){
return {
restrict: "A",
templateUrl: 'templates/businessTemplate.html',
replace: true,
transclude: false,
scope: {
businessID: '=topbar'
}
}
})
payments.html
<h1>Payments</h1>
<section ng-controller="paymentController">
<div>
<div class="ui center aligned grid">
<div class="ui buttons">
<button class="ui button" ng-click="selectBusiness()">Business</button>
<button class="ui button arrow" ng-click="selectPerson()">Person</button>
</div>
<div class="ui input" ng-keypress="submit()">
<input id="businessID" type="text" ng-autocomplete ng-model="autocomplete">
</div>
<div class="submit">
<button class="ui button" id="submit" ng-click="addPayee()">
<i class="arrow right icon"></i>
</button>
</div>
</div>
<div class="search"></div>
<div class="payments" ng-controller="paymentController">
<li ng-repeat="newPayee in payees">{{payees}}</li>
</div>
<!-- <topbar></topbar> -->
</div>
(example template)
businessTemplate.html:
<div class="Business">
<div class="BusinessName" id="name">{{businessID}}</div>
<div class="Address" id="address">{{address}}</div>
<button class="ui icon button" id="hoverbox">
<i class="dollar icon"></i>
</button>
</div>

I ended up using a workaround with ng-repeat here. Still curious about the original question though.

Related

AngularJS: Bind data returned from a modal to row it was clicked from

Using AngularJS here.
I am working on a UI which has a dropdown. Based on what the user selects I show 2 tabs to the user.
Each tab data is returned from a service which just returns an array of data (string).
Against each string value returned I show a button against it. When you click the button it opens a modal popup where the user can select some data.
When they close the modal I return the data back to the controller.
The normal flow of binding data to tab, opening modal and returning data from modal all works fine.
What I am not able to understand or design is how to bind the returned data against the button or row which it was clicked from
For example as below:
Tab1
String1 - Button1
String2 - Button2
String3 - Button3
If I open the modal by clicking button1, how do I find out button1 was pressed and bind back data that was returned from its modal.
Some of the relevant code as below:
<div id="params" ng-if="type.selected">
<tabset class="tabbable-line">
<tab heading="Sets" ng-if="sets" active="tab.set">
<div class="form-group m-grid-col-md-8 param" style="margin-top:5px"
ng-repeat="set in sets" >
<label class="control-label col-md-3 param-label">{{set}}
</label>
<button ng-click="openModal()" class="btn btn-info btn-sm">
Select
</button>
</div>
</tab>
<tab heading="Tables" ng-if="tables" active="tab.table">
<div class="form-group m-grid-col-md-8 param"
ng-repeat="table in tables">
<label class="control-label col-md-3 param-label">{{table}}
</label>
<button ng-click="openModal()" class="btn btn-info btn-sm">
Select
</button>
</div>
</tab>
</tabset>
</div>
Controller:
$scope.onChangeType = function (selectedValue) {
$scope.getData(selectedValue);
};
$scope.getData = function (selectedValue) {
//Commenting out the service part for now and hardcoding array
// service.getData(selectedValue).then(function (res) {
$scope.sets = ['Set1', 'Set2', 'Set3'];
$scope.tables = ['Table1', 'Table2'];
// });
};
$scope.openModal = function () {
myFactory.defineModal().then(function (response) {
//how to bind data from response
});
};
I have created a plnkr for this sample as:
http://plnkr.co/edit/vqtQsJP1dqGnRA6s?preview
--Edited--
<div class="form-group m-grid-col-md-8 param" ng-repeat="table in tables">
<label class="control-label col-md-3 param-label">{{table}}
</label>
<button ng-click="openModal(table)" class="btn btn-info btn-sm">
Select
</button>
<span>
{{table.utype}}
</span>
</div>
Pass the table object as an argument to the openModal function:
<button ng-click="openModal(table)">Select</button>
Use it in the openModal function:
$scope.openModal = function (table) {
myFactory.defineModal().then(function (result) {
table.utype = result.utype;
table.minvalue = result.minvalue;
});
};
Be sure to close the modal with the result:
$scope.ok = function () {
var result = {
utype: $scope.utype,
minvalue: $scope.minvalue,
};
$modalInstance.close(result);
};
Keep in mind that modals are considered disruptive and are despised by user.
Generally speaking, disruptions and distractions negatively affect human performance, a common finding in cognitive psychology. Many studies have shown that distraction greatly increases task time on a wide variety of tasks.
For more information, see
What research is there suggesting modal dialogs are disruptive?
While I dont get any error not but I dont get the text returned.
Be sure to furnish objects to the ng-repeat:
$scope.getData = function (selectedValue) {
//Commenting out the service part for now and hardcoding array
// service.getData(selectedValue).then(function (res) {
̶$̶s̶c̶o̶p̶e̶.̶t̶a̶b̶l̶e̶s̶ ̶=̶ ̶[̶'̶T̶a̶b̶l̶e̶1̶'̶,̶'̶T̶a̶b̶l̶e̶2̶'̶]̶;̶
$scope.tables = [
{name:'Table1',},
{name:'Table2',},
];
// });
};
The DEMO on PLNKR
Try to pass the table to openModal in your template
<button ng-click="openModal(table)"
Now you can use it as a reference in your openModal function
$scope.openModal = function (table) {
// table === the clicked table
}

Apply $scope ng-click event to hidden element after it is displayed

Please ask me for better explanation. I have built a global search function into the header of my site. I want to display a separate input box for mobile search that uses the same ng-click event but the input isn't displayed when the page loads. I am having trouble getting the hidden input value on the mobile ng-click once it is displayed.
The areas of concentration are the search click function is not finding the correct ng-model when the function is triggered. I think it is because since the hidden elements are not available on load the ng-model="searchQueryStringMobile" isn't applied to the scope somehow.
My question is how do I get ng-model="searchQueryStringMobile" applied in the scope after it has been displayed posthumously or post-click ng-click="flipNav('search')" so that it does not return undefined when you activate the ng-click="loadSearchResults"?
Here is the code:
HTML:
<div ng-controller="HeaderCtrl as header" class="container">
<div id="jesusSearchTop">
<input ng-model="searchQueryString" class="jesusSearchInput autoCompSearch" type="search" placeholder="Search..." autocomplete="off" />
<select ng-model="searchDDL.item" class="jesusSearchSelect" ng-options="item.name for item in searchDDL track by item.id"></select>
<div class="jesusSearchHolder">
<img class="goSearch" ng-model="jesusSearch" ng-click="loadSearchResults('norm')" src="/EMR4/img/gui_icons/searchIcon.png" alt="Search EMR" />
</div>
</div>
<div id="siteControls">
<div id="siteSearch" class="siteControl" ng-click="flipNav('search')"></div>
</div>
<div ng-switch="dd" class="dropDown">
<div ng-switch-when="none" style="display:none"></div>
<div ng-switch-when="search" class="dropMenu listStyle4" id="Search">
<input ng-model="searchQueryStringMobile" class="jesusSearchInput" type="text" placeholder="Search..." autocomplete="off" />
<select ng-model="searchDDL.item" class="jesusSearchSelect" ng-options="item.name for item in searchDDL track by item.id"></select>
<div class="jesusSearchHolder">
<img class="goSearch" ng-model="jesusSearchMobile" ng-click="loadSearchResults('mob')" src="/EMR4/img/gui_icons/searchIcon.png" alt="Search EMR" />
</div>
</div>
</div>
<div class="clr"></div>
</div>
Controller:
app.controller('HeaderCtrl', function ($scope, $http, $location, populateDDL) {
$http.get(badge.credentials[7].home+'data.JSON')
.success(function(data, status, headers, config) {
$scope.header = data.header;
$scope.searchOptions = new populateDDL('tblWebMenuItems',badge.credentials[1].key).
then(function(response) {
$scope.searchDDL = response.tblWebMenuItems
$scope.searchDDL.item = $scope.searchDDL[0];
});
})
.error(function(data, status, headers, config) {
console.log(data+', '+status+', '+headers+', '+config);
});
$scope.flipNav = function(choice){
if ($scope.dd === choice) {
console.log(choice);
$scope.dd = "none";
}else {
$scope.dd = choice;
}
};
$scope.loadSearchResults = function(uv) {
var loader;
if (uv === "mob") {
loader = $scope.searchQueryStringMobile;
}else if (uv === "norm") {
loader = $scope.searchQueryString;
}
console.log(uv+' - '+loader);
if (loader == null || loader < 2) {
alert('Please refine your search and continue, Thank you!');
}else {
$location.path("/search/"+$scope.searchDDL.item.name.toLowerCase()+"/");
$location.search("type",$scope.searchDDL.item.name.toLowerCase());
$location.search("query", loader);
}
};
});
i have tested your code and found that it is because of the ng-switch.As ng-switch creates its own new scope which is child scope of its parent's, so if you use ng-model=$parent.searchQueryStringMobile , then it will work fine or If you use ng-show instead of ng-swtich ,it will work because ng-show doesnt create new child scope, it just sets the markup's css property display to noneand $parent allows you to access items of parent scope from child scope.In your example, $scope.searchQueryStringMobile is in the parent scope of ng-switch's scope. Here is the working plunk click
you can change your ng-switch markup to this
<div ng-switch="dd" class="dropDown" >
<div ng-switch-when="none" style="display:none"></div>
<div ng-switch-when="search" class="dropMenu listStyle4" id="Search">
<input ng-model="$parent.searchQueryStringMobile" class="jesusSearchInput" type="text" placeholder="Search..." autocomplete="off" />
<select ng-model="searchDDL.item" class="jesusSearchSelect" ng-options="item.name for item in searchDDL track by item.id"></select>
<div class="jesusSearchHolder">
<img class="goSearch" ng-model="jesusSearchMobile" ng-click="loadSearchResults('mob')" src="/EMR4/img/gui_icons/searchIcon.png" alt="Search EMR" />
</div>
</div>
</div>
Notice the ng-model for the input element in the above code.
Your problem is quite simple one. ng-switch just like ng-if creates new scope, so when you are using ng-model, yu are assign property to this new scope and not the scope used by your controller.
Solution would be to use controller as syntax or use property of some object created on scope. To illustrate this I create example for you.
As you can see {{a}} does not work outside new scope, but {{x.b}} works just fine.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-init="x = {}; show = 'first'">
<button type="button" ng-click="show = 'first'">show first</button><br>
<button type="button" ng-click="show = 'second'">show second</button><br>
a = {{a}}<br>
x.b = {{x.b}}
<div ng-switch="show">
<div ng-switch-when="first">
<input type="text" ng-model="a">
</div>
<div ng-switch-when="second">
<input type="text" ng-model="x.b">
</div>
</div>
</div>

Adding objects to the DOM after adding new data to the list

I have an array of objects which i populate on a button click.
When populating this array i make sure that i only add 10 objects to it.
When this is all loaded in the dom i give the user the oppertunity to add a few more objects.
I do this like this:
$scope.Information = [];
$.each(data, function (i, v) {
if (i<= 9)
$scope.Information.push(data[i]);
if(i >= 10) {
cookieList.push(data[i]);
}
}
if (cookieList.length) {
localStorage.setItem("toDoList", JSON.stringify(cookieList));
$(".showMore").removeClass("hidden");
}
$(".showMore").on("click", function() {
var obj = JSON.parse(localStorage.getItem("toDoList"));
console.log(obj);
console.log(obj.length);
SetSpinner('show');
$scope.Information.push(obj);
SetSpinner('hide');
//$.removeCookie("toDoList2");
});
part of the HTML:
<div ng-repeat="info in Information" class="apartment container" style="padding-right:35px !important">
<div class="row" style="height:100%">
<div class="col-md-1 col-xs-12">
<div>
<h4 class="toDoListHeadings">Nummer</h4>
<div style="margin-top: -15px; width:100%">
<span class="toDoListItems number">
{{info.orderraderid}}
</span>
</div>
</div>
</div>
</div>
</div>
My issue: When i add objects to my array of objects "$scope.Information.push(obj);" I assumed that they would get added in the DOM but they do not, how do i do this the angular way?
EDIT MY SOLOUTION:
edited the HTML to use ng-click and the method is as follows:
$scope.addMore = function() {
var obj = JSON.parse(localStorage.getItem("toDoList"));
SetSpinner('show');
$.each(obj, function(i,v) {
$scope.Information.push(v);
});
SetSpinner('hide');
}
Here is the angular way:
 The view
<!-- Reference your `myapp` module -->
<body data-ng-app="myapp">
<!-- Reference `InfoController` to control this DOM element and its children -->
<section data-ng-controller="InfoController">
<!-- Use `ng-click` directive to call the `$scope.showMore` method binded from the controller -->
<!-- Use `ng-show` directive to show the button if `$scope.showMoreButton` is true, else hide it -->
<button data-ng-click="showMore()" data-ng-show="showMoreButton">
Show more
</button>
<div ng-repeat="info in Information" class="apartment container" style="padding-right:35px !important">
<div class="row" style="height:100%">
<div class="col-md-1 col-xs-12">
<div>
<h4 class="toDoListHeadings">Nummer</h4>
<div style="margin-top: -15px; width:100%">
<span class="toDoListItems number">
{{info.orderraderid}}
</span>
</div>
</div>
</div>
</div>
</div>
</section>
</body>
The module and controller
// defining angular application main module
var app = angular.module('myapp',[])
// defining a controller in this module
// injecting $scope service to the controller for data binding with the html view
// (in the DOM element surrounded by ng-controller directive)
app.controller('InfoController',function($scope){
$scope.Information = [];
$scope.showMoreButton = false;
// Bind controller method to the $scope instead of $(".showMore").on("click", function() {});
$scope.showMore = function(){
var obj = JSON.parse(localStorage.getItem("toDoList"));
console.log(obj);
console.log(obj.length);
SetSpinner('show');
$scope.Information.push(obj);
SetSpinner('hide');
//$.removeCookie("toDoList2");
};
$.each(data, function (i, v) {
if (i<= 9) $scope.Information.push(data[i]);
if(i >= 10) cookieList.push(data[i]);
});
if (cookieList.length) {
localStorage.setItem("toDoList", JSON.stringify(cookieList));
//$(".showMore").removeClass("hidden");
$scope.showMoreButton = true; // use $scope vars and ng-class directive instead of $(".xyz").blahBlah()
}
});
You should not use JQuery, use ng-click to detect the click, because angular has no idea when JQuery is done and when it needs to refresh the interface

Reset data on click for a different controller

I have two divs - the first contains the second. The contained div has its own controller. When I click an icon button in the container, I change a variable which then affects the visibility of the contained div.
It looks like this:
<div ng-controller="BarController">
<div class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<div class="col-lg-2 page-title">My Page</div>
<div class="col-lg-10">
<span class="actions">
<i class="fa fa-lg fa-download fa-inverse" tooltip="Download"
ng-click="showSecondaryBar=!showSecondaryBar"></i>
</span>
</div>
</div>
</div>
<div class="download navbar download-in download-out"
ng-class="{'myhidden': !showSecondaryBar}"
ng-cloak>
<div class="col-lg-offset-4 col-lg-4 form-inline form-group" ng-controller="TagsController">
<div class="download-label col-lg-6">
<label>Download by tags:</label>
</div>
<div class="download-tags col-lg-6">
<tags-input class="bootstrap" spellcheck="false" min-length="1" ng-model="tags" add-from-autocomplete-only="true">
<auto-complete source="loadTags($query)" min-length="1" load-on-down-arrow="true"
load-on-focus="true" max-results-to-show="5"
highlight-matched-text="false"></auto-complete>
</tags-input>
</div>
</div>
</div>
</div>
The <tags-input> is taken from ng-tags-input and I would like to reset the tags that were already typed to it whenever the icon button is clicked (which changes the visilibyt of the div that contains the ng-tags-input).
Problem is, because I have the TagsController which contains the data (tags) and this data is not visible in the BarController, I'm not sure how I can reset the tags array to become empty.
I thought of using a service but it fills like too much of a coupling. I would prefer to have a function in TagsController which is called upon click. But I can't figure out how to do it from another controller
You are right you have to use a service.
Why don't you use a broadcast as your TagsController is included in BarController?
You can include a scope.broadcast("Event") in BarController
Then a "on" listener on TagsController who will reset the tags array when "Event" Occur.
I would personnaly to this.
https://docs.angularjs.org/api/ng/type/$rootScope.Scope
You can use $broadcast on $rootScope to send an event to TagsController. So TagsController can receive this event by registering an event listener for it. See following example.
Refer to $rootScope API docs
angular.module('app',[])
.controller('ParentController', function($rootScope) {
var parentCtrl = this;
parentCtrl.someFlag = true;
parentCtrl.changeFlag = function() {
parentCtrl.someFlag = !parentCtrl.somFlag;
$rootScope.$broadcast('resettags', {'defaultTags': 'whatever_tag'});
}
})
.controller('ChildController', function($rootScope){
var childCtrl = this;
childCtrl.tags = "Some tags entered by user";
$rootScope.$on('resettags', function(event, args) {
childCtrl.tags = args.defaultTags;
});
});
.myHidden {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div id="main" ng-controller="ParentController as parentCtrl">
<button type="button" ng-click="parentCtrl.changeFlag()">Toggle</button>
<div ng-class="{'myHidden' : !parentCtrl.someFlag}">
<div ng-controller="ChildController as childCtrl">
<h1>{{childCtrl.tags}}</h1>
</div>
</div>
</div>
</div>

AngularJS directive to add other directives not functional

I am trying to construct a directive that adds form groups to a particular div. I'm attempting doing this by binding a directive to a button in my html. My application is VERY simple as this is all I'm trying to do at the moment. Similar to this fiddle Anyway, my app initiates fine and the home controller is included. Also I get 200 status codes on the inclusion of my directive, and my code throws no errors. Here is my html:
<form id="addFields">
<div class="row">
<div class="col-xs-4">
</div>
<div class="col-xs-4 text-center">
<div addInputFieldsButton></div>
<input id="fieldName" placeholder="Enter field name:"></input>
<button id="addFieldBtn" addInputFields><span class="glyphicon glyphicon-plus"></span></button>
</div>
<div class="col-xs-4">
</div>
</div>
</form>
<div id="reviewFields">
</div>
Notice I am attempting both to add the button that is supposed to add input fields, and just bind the directive that adds input fields to an existing button as an attribute. Neither work.
addInputFields directive:
(function () {
angular.module('reviewModule')
.directive('addInputFields', addInputFields);
addInputFields.$inject = ['$log'];
function addInputFields ($log) {
return function (scope, element, attr) {
$log.debug('binding click event to add review button now.');
element.bind('click', function ($compile) {
$log.debug('button bound.');
angular.element(document.getElementById('reviewFields')).append($compile("<button>YOU MADE A BUTTON, COOL BRO</button>")(scope));
});
}
}
})()
and my directive for attempting to add a button that has the above binding:
(function () {
angular.module('reviewModule')
.directive('addInputFieldsButton', addInputFieldsButton);
addInputFieldsButton.$inject = ['$log'];
function addInputFieldsButton ($log) {
return {
restrict : 'E',
template : '<input id="fieldName" placeholder="Enter field name:"></input>\
<button id="addFieldBtn" addInputFields><span class="glyphicon glyphicon-plus"></span></button>'
};
};
})()
I copied the fiddle almost exactly, and really have no idea why nothing is happening while attempting to use either of these directives. Forgive me if my error is obvious, I am still pretty new to AngularJS.
I believe your second directive is not defined correctly on UI, it should be - separated with smaller case add-input-fields instead of addInputFields.
Code
(function () {
angular.module('reviewModule')
.directive('addInputFieldsButton', addInputFieldsButton);
addInputFieldsButton.$inject = ['$log'];
function addInputFieldsButton ($log) {
return {
restrict : 'E',
template : '<input id="fieldName" placeholder="Enter field name:"></input>\
<button id="addFieldBtn" add-input-fields><span class="glyphicon glyphicon-plus"></span></button>'
//^^^^^^^^^^^^^^^here is change
};
};
})()

Categories

Resources