bind controller on dynamic HTML - angularjs

I have a HTML page with different sections, which are loaded with AJAX. I have created a controller for each of the sections.
How is possible to bind the controller on a section which has been dynamically added on HTML?
I have found very complicated solutions, which i don't even know if they apply.
I need the most basic, easiest solution, something similiar with ko.applyBindings($dom[0], viewModel) for the ones who worked with KnockoutJs.
Index html
<div class="row" ng-app="app">
<div class="col-xs-3">
<ul class="nav nav-pills nav-stacked">
<li>
Profile
</li>
</ul>
</div>
<div class="col-xs-9">
<div id="container"><!-- load dynamic HTML here --></div>
</div>
</div>
Dynamic HTML
<div ng-controller="profile">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</div>
Javascript:
var app = angular.module('app', []);
app.controller('profile', function ($scope) {
$scope.firstName = "John";
$scope.lastName = "Doe";
});
// load new HTML
// normally this is triggered by a link / button
$(function () {
$.get("/EditProfile/Profile", function (data, status) {
$("#container").html(data);
});
});

Don't use jQuery to embed html to your container. If you use jQuery, AngularJS can't track DOM manipulations and trigger directives. You can use ng-include directive of AngularJS.
In index.html file:
...
<div id="container">
<div ng-include="'/EditProfile/Profile'"></div>
</div>
...
If you want you can make that conditional:
...
<div id="container">
<div ng-if="profilePage" ng-include="'/EditProfile/Profile'"></div>
</div>
...
With that example, if you set profilePage variable to true, profile html will be load and render by ng-include.
For detailed info take a look to ngInclude documentation.
By the way, best practice to include views to your layout by triggering same link clicks is using routers. You can use Angular's ngRoute module or uiRouter as another popular one.
If you use ngRoute module, your index.html looks like that:
<div class="row" ng-app="app">
<div class="col-xs-3">
<ul class="nav nav-pills nav-stacked">
<li>
Profile
</li>
</ul>
</div>
<div class="col-xs-9">
<div id="container" ng-view><!-- load dynamic HTML here --></div>
</div>
</div>
And with a router configuration like below, profile html will be automatically loaded and rendered by ngRoute inside ngView directive:
angular.module('myapp', []).config(function($routeProvider) {
$routeProvider
.when('/profile', {
templateUrl: '/EditProfile/Profile',
controller: 'ProfileController'
});
});

Related

Angularjs ng-hide not working with ng-include template

Can anybody help me.
In index.html, I run ng-view, which code corresponds to Routing.js file. The menu calls two pages main.html and product.htm.
main.htm, has included another page called filterApp.html.
I need to hide a page element that is embedded main.html the filterApp.html. I have not succeeded.
If I take the include and put the code directly into main.html works. I read that ng-ng-hide and include not working properly. Is there anything I can do. I need to have it in separate files.
Here is the code of the pages.
Thank you so much
Index.html
<html ng-app="appDataBoard">
<body ng-controller="mainController" >
<ul >
<li class="nav-item start open">
</li>
<li class="nav-item ">
</li>
</ul>
<div class="col-md-12 column sortable">
<div ng-view></div>
</div>
</body>
</html>
Main.html
<div class="jumbotron text-center">
<h1>main Page</h1>
<p>{{ message }}</p>
</div>
<!-- INCLUDE FILTER APPS-->
<div ng-include="'./template/filterApp.html'" ></div>
Product.html
<div class="jumbotron text-center">
<h1>Product Page</h1>
<p>{{ message }}</p>
p ng-hide="hide1">este parrafo se debe borrar</p>
</div>
filterApp.html
<div ng-hide="hselect1" >
<select id="select-1" multiple="multiple" onchange="RecargaSelectBU()" >
</select>
</div>
Routing.js
// create the module and name it dbApp
var dbApp = angular.module('appDataBoard', ['ngRoute']);
// configure our routes
dbApp.config(function($routeProvider) {
$routeProvider
// route for the home page
.when('/main', {
templateUrl : 'template/main.html',
controller : 'mainController'
})
// route for the about page
.when('/product', {
templateUrl : 'template/product.html',
controller : 'productController'
})
});
// create the controller and inject Angular's $scope
dbApp.controller('mainController', function($scope) {
// create a message to display in our view
$scope.message = 'Pagina principal';
$scope.hselect1 = true;
});
dbApp.controller('productController', function($scope) {
$scope.message = 'Pagina de producto.';
$scope.hide1 = true;
});
Check this plunker link. Its working fine for me.
https://embed.plnkr.co/RZDGdnFEq1SmT7F3ncZa/

Hyperlinks with Angular Route / MVC Area not working

I have a bit of a complicated MVC project that we are adding some Angular capability to. I am very new to Angular and have watch three Angular courses on Pluralsight now. They have been very helpful but I am missing some piece of understanding.
I understand that I need to have just one ng-app (unless I want to bootstrap and I don't think I want to). So this app has defined my config and a custom directive. The custom directive is working nicely but when I click on a link in that directive it does not go anywhere....well perhaps it does but not where I am expecting. Fiddler shows nothing unusual like 404 or 500 errors.
(function ()
{
"use-strict";
angular.module("appMainNav", ["ngRoute"])
.config(function ($routeProvider) {
$routeProvider.when("/MySearch", {
controller: "routingController"
});
$routeProvider.otherwise({ redirectTo: "/" });
})
})();
My routingController:
(function () {
"use strict";
angular.module("appMainNav")
.controller("routingController", routingController);
function routingController($http) {
var vm = this;
alert("Routed");
}
})();
My HTML link as rendered:
Search
I don't get a page not found...the url just appends the #/MySearch. No console errors etc.
I was expecting to get a javascript alert...?
TIA
Edit
Should have added sooner. Here is markup:
_Layout page:
<body>
<div ng-app="appMainNav" ng-controller="routingController" class="container">
#RenderBody()
</div>
</body>
RenderBody is Index.cshtml:
<div class="row">
<div>
Header Stuff
</div>
</div>
<div class="row">
<div class="col-md-2">
<dashboard-main-nav></dashboard-main-nav>
</div>
<div class="col-md-3">
<div></div>
</div>
<div class="col-md-8">
<div></div>
</div>
</div>
<div class="row">
<div>
Footer Stuff
</div>
</div>
The custom directive angular code was left out of the snips above however that is where the link is generated.
You need to assign an event handler to a click event on the anchor tag:
Search
where foo is a function defined on the controller's scope:
function routingController($http, $scope) {
var vm = this;
$scope.foo = function(){
alert("I am so tired");
}
alert("Routed");
}
Try that and it should work.

AngularJS multi-step form validation using ui-router module

I have a form that is spread over multiple tabs. Originally, I used the tabset directive from ui-bootstrap, but then came the requirement to deep link to a specific tab, so I thought I'd use nested views from ui-router.
The problem I have is that the parent form is only valid when all the sub-forms are valid, but using ui-router states, only one sub-form is loaded at a time.
Here's an example to clarify
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="//code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
<script src="script.js"></script>
</head>
<body class="container-fluid">
<div ui-view>
<ul class="list-unstyled">
<li><a ui-sref="edit.basic">Basic</a></li>
<li><a ui-sref="edit.extended">Extended</a></li>
</ul>
</div>
</body>
</html>
script.js
angular.module('app', ['ui.router']).
config(function($stateProvider) {
$stateProvider.state('edit', {
abstract: true,
url: '/edit/',
templateUrl: 'edit.html',
controller: function($scope) {
$scope.model = {};
}
}).
state('edit.basic', {
url: '/basic',
templateUrl: 'basic.html'
}).
state('edit.extended', {
url: '/extended',
templateUrl: 'extended.html'
});
});
edit.html
<div class="row" ng-form="editForm">
<div class="col-xs-12">
<ul class="nav nav-tabs">
<li ui-sref-active="active">
<a ui-sref="edit.basic">Basic</a>
</li>
<li ui-sref-active="active">
<a ui-sref="edit.extended">Extended</a>
</li>
</ul>
</div>
<div class="col-xs-12" ui-view></div>
<div class="col-xs-12">
<button type="button" class="btn btn-primary" ng-disabled="editForm.$invalid">Save</button>
</div>
<div class="col-xs-12">
<hr>
<tt>model = {{model}}</tt><br>
<tt>editForm.$valid = {{editForm.$valid}}</tt><br>
<tt>editForm.basicForm.$valid = {{editForm.basicForm.$valid}}</tt><br>
<tt>editForm.extendedForm.$valid = {{editForm.extendedForm.$valid}}</tt>
</div>
</div>
basic.html
<div ng-form="basicForm">
<div class="form-group">
<label for="textProperty">Text Property</label>
<input type="text" class="form-control" name="textProperty" id="textProperty" ng-model="model.textProperty" required>
</div>
</div>
extended.html
<div ng-form="extendedForm">
<div class="form-group">
<label for="numericProperty">Numeric Property</label>
<input type="number" class="form-control" name="numericProperty" id="numericProperty" ng-model="model.numericProperty" required>
</div>
<div class="form-group">
<label for="dateProperty">Date Property</label>
<input type="date" class="form-control" name="dateProperty" id="dateProperty" ng-model="model.dateProperty" required>
</div>
</div>
I am beginning to believe that this approach is not suitable for my purpose. Instead of using nested views, I should continue to use the tabset and use a state parameter to activate the appropriate tab.
I'm interested to know how others would solve it.
UPDATE 1:
Here is one solution I came up with that uses the ui-bootstrap tabset but doesn't use nested ui-router states and instead parameterises the active tab.
UPDATE 2:
Here is a solution which uses nested states along with a validator service following the suggestion of Santiago Rebella
UPDATE 3:
Here is an alternate solution to update 2 which uses a validator object on the parent state's scope for comparison
You could try to do it in the way you state just passing to a service some kind of attributes like step1, step2, etc and go adding true or whatever your success value you may use, so once all those attributes are true, in your ng-disabled or the way you are building your form, is allowed to be submitted.
Maybe previous formsteps you could be storing the values of the inputs in an object in your service. I think in this way you could a multi-page form.
controller.js
var app = angular.module('employeeDemoApp');
app.controller('employeesCtrl', function($scope, $state, EmployeeService, $stateParams) {
console.log("Hello")
$scope.saveEmployee = function(formname){
console.log(formname.$error)
EmployeeService.saveEmployee($scope.user);
$state.go("employees");
}
$scope.employeeslist = EmployeeService.getEmployees();
if($stateParams && $stateParams.id){
$scope.user = EmployeeService.getEmployee($stateParams.id);
$scope.user.dob = new Date($scope.user.dob);
console.log("gdfg", $scope.user)
}
$scope.updateEmployee = function(req){
EmployeeService.updateEmployee($stateParams.id, $scope.user);
$state.go("employees")
}
$scope.goto_new_employee_page = function(){
$state.go("employees_new")
}
$scope.deleteEmployee = function(index){
console.log("fgdfhdh")
EmployeeService.deleteEmployee(index);
$scope.employeeslist = EmployeeService.getEmployees();
}
});

AngularJS - Create a dynamic, generic header

In my AngularJS application, I'm using a router with an ng-view directive.
Besides it I would like to add a generic header (same for all the views). So I did the following:
<!-- index.html -->
<body ng-app="myApp">
...
<div ng-controller="TopmenuCtrl" class="header">
<div ng-include="template.url"></div>
...
</div>
<div ng-view></div>
</body>
The view is dynamic depending on the session token:
<!-- views/topmenu.html -->
<ul class="nav nav-pills pull-right">
<li class="active"><a ng-href="#">Home</a></li>
<li><a ng-href="#">About</a></li>
<li><a ng-href="#">Contact</a></li>
<li ng-show="token"><a ng-href="#" ng-click="doLogout()">Logout</a></li>
</ul>
And the topmenu controller with the logout method:
// controllers/topmenu.js
$scope.template = {url: 'views/topmenu.html'};
$scope.doLogout = function() {
localStorageService.clearAll();
$window.sessionStorage.token = '';
$location.path('/login');
};
The problem is: When I click on "logout" in the app, the topmenu controller is called and destroy the session but the main one from the router is called too and display an error because the session was destroyed!
The only (not satisfactory) solution I found to prevent this is to add this code in each controllers of the app:
if ($window.sessionStorage.token = '') {
return;
}
Is there a way to execute the header controller but not the main ng-view controller?
I finally found my answer: https://stackoverflow.com/a/11672909/900416.
Now my logout link looks like: <a href ng-click="doLogout()">Logout</a>.

AngularJS - "10 $digest() iterations reached" when ng-view ng-repeat dependant on $routeParams

I am really new to Angular and this is the first time that I am dealing with routing, so please excuse me if my questions are a bit confusing. My actual logic and structure for this app is much more complex and I am using several services, but this is the general idea of the part that I am having issues with. I would really APPRECIATE your help! Thanks.
SOLVED
I was adding the same template recursively. Inside the template, I changed that ng-repeat template to another one and it worked perfectly. http://jsfiddle.net/GeorgiAngelov/9yN3Z/111/
UPDATED
jsFiddle: http://jsfiddle.net/GeorgiAngelov/9yN3Z/106/
I updated my fiddle thanks to Chandermani ( I removed all the controllers and left only one copy of it) but now I get "10 $digest() iterations reached" whenever I click on the Add Root button OR whenver I click on a section it keeps giving me that error... No idea why.
So I have an app where I have a menu on my left side where I can add more elements called sections to that menu. That menu section item is derived from an array called sections that sits in my controller and when you add a section it adds it to the array named sections.
$scope.sectionId = $routeParams.sectionId;
$scope.sections = [];
$scope.counter = 1;
$scope.section = {};
$scope.addSection = function(){
$scope.sections.push({
id: $scope.counter++,
name: 'Random Name',
roots: []
});
};
*HTML for sections *
<body ng-app="myApp" ng-controller="MyController">
<div class="tabbable tabs-left pull-left">
<ul class="nav nav-tabs">
<li ng-repeat="section in sections" ng-click="setSectionSelected(section)" ng-class="{active : isSectionSelected(section)}">{{section.name}}</li>
</ul>
<button class="addSection btn btn-success" ng-click="addSection()" style="position:relative; bottom: 0;">Add Section</button>
</div>
<div >
<div class="tab-pane" ng-class="{active : isSectionSelected(section)}" id="{{section.id}}" ng-repeat="section in sections">
</div>
</div>
<div ng-view></div>
</body>
Every object in that array has another array called roots and with another button, named Add Root, I want to push objects(elements) to whatever section I am currently on, depending on the $routeParams id variable.
$scope.addRoot = function(section){
section.roots.push({
id: $scope.counter++,
name: 'Random Root',
});
};
HTML template that is called from the router
<script type="text/ng-template" id="/tpl.html">
<div class="map-container">
<div class="map">
<h2>{{section.name}}</h2>
<h4>{{section.description}}</h4>
<button class="add btn btn-success" ng-click="addRoot(section)">Add Root</button>
<div ng-repeat="root in section.roots" ng-include="'/tpl.html'"></div>
</div>
</div>
</script>
app.config(function ($routeProvider) {
$routeProvider
.when('/section', {
templateUrl: '/tpl.html',
sectionId: '1',
})
.when('/section/:sectionId', {
templateUrl: '/tpl.html',
})
.otherwise({
redirectTo: '/'
});
});

Resources