jquery $ selector to apply only to template scope (AngularJS) - javascript

I'm generating a template multiple times in the same page, however a function of this template's controller will work on the whole page. How can make sure it only works within the respective template scope?
In this example, when the user clicks a small div, its content it will travel to another div. How does this work? I select the clicked div with a specific class name using jquery and append it to the div it should travel to. However, as I'm calling this template multiple times, the function will select all classes in the page with that same class name, instead of just the template scope.
How can I make it that the selector will only look into the template's scope?
Here's the template:
<div ng-controller="templateController">
<div class="source">
<div ng-repeat="item in info">
<div class="content" data-value="{{item.ID}}">{{item.name}}</div>
</div>
</div>
<br style="clear:both" />
<div class="receiver"></div>
<div>
</div>
And here's the controller with some jquery functions:
angular
.module("demo")
.controller("templateController", ["$scope", "$timeout", function($scope, $timeout) {
$scope.info = [
{
name: "john",
ID: 1
},
{
name: "Edward",
ID: 0
},
{
name: "Carl",
ID: 2
}
];
$timeout(function () {
$(".source .content").click(function () {
console.log("leaving source");
$(this).appendTo($(".receiver"));
})
});
$timeout(function () {
$(".receiver .content").click(function () {
console.log("leaving receiver");
$(this).appendTo($(".source"));
})
});
I also would like to travel back to its original container (as shown in the second function but seems to not be working), but it only travel in one direction.
Here's a simple plunk with the whole code so you can see it work and see what's wrong
enter link description here
Thank you

Related

Sharing scope in different views with custom controllers

I've got the next problem and I'd like to find a solution or if I should even be doing this like I am.
I have the following ui-router configuration:
$stateProvider.state('ListCounterparties', {
url:"/counterparties",
data: { NavMenuItem : 'Counterparties / Desks' },
views: {
'' : {
templateUrl:"./app/module_Counterparties/templates/ListCounterparties.html",
controller:"CounterpartiesControllerCtrl"
},
'deskLists#ListCounterparties' : {
templateUrl : './app/module_Counterparties/templates/DesksDualListTemplate.html',
controller:'DesksControllerCtrl'
}
}
The first, unnamed view, has a table from which I can select a row and then a method will be called to populate a dual list from the second view.
Up until now, I've had both in the same controller, but the controller is getting too big and I decided I had to separate them.
The method to populate the dual lists in 'deskLists#ListCounterparties' is defined in DesksControllerCtrl but it should be called in CounterpartiesControllerCtrl, as the event of row selection is in that controller.
The problem is that the scopes are not shared and the method is inaccesible to the unnamed view.
Accessing the scope in DesksControllerCtrl I could see that accessing the $parent property twice I can get to the CounterpartiesControllerCtrl, but I don't thin that's an ideal thing to do.
Thanks in advance.
Sharing data, methods, etc. between multiple controllers the Angular way would be to create service(s). That means, you create e.g. a service which holds all your data and another one which provides functionality for several controllers. Then, just inject them in your controllers:
var myApp = angular.module('myApp', []);
myApp.factory('myGlobalData', function() {
return {
data: 1
};
});
myApp.factory('myService', function(myGlobalData) {
return {
increment: function() {
myGlobalData.data++;
}
};
});
myApp.controller('MyCtrlA', function($scope, myGlobalData, myService) {
$scope.myGlobalData = myGlobalData;
$scope.myService = myService;
});
myApp.controller('MyCtrlB', function($scope, myGlobalData, myService) {
$scope.myGlobalData = myGlobalData;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<p ng-controller="MyCtrlA">
{{myGlobalData.data}}
</p>
<p ng-controller="MyCtrlB">
{{myGlobalData.data}}
</p>
<div ng-controller="MyCtrlA">
<button ng-click="myService.increment()">Increment data in myGlobalData service</button>
</div>
</div>

ng-repeat inside Custom Angular Directive Not Working

I am attempting to build what should be a very simple custom directive using some hierarchical data. Each page in the list has subpages and the data is of the form:
{"Title 1": "Page Title", "Id": "1", "Pages": {"Title 1.1": "Page Title 1.1", "Id": "2"}, {"Title 1.2": "Page Title 1.2", "Id": "3"}}
and so on. The data here is just a quick illustration - there is no issue with either the data or retrieval method.
I have a directive controller set up as:
app.directive('pageSelectList', function () {
return {
restrict: 'EA',
replace: true,
scope: {
pageList: '='
},
templateUrl: '/Scripts/App/Directives/Templates/PageSelectList.html',
link: function (scope, elem, attrs) { }
};
});
The template is:
<ul class="page-list page-root">
<li ng-repeat="p in pageList.Pages">{{ p.Title }}</li>
</ul>
The directive is used with data drawn from the parent scope ($scope.listOfPages).
However, when using it, nothing is displayed - it's blank. Strangely, when replacing the directive template with the following markup:
<p>{{ pageList.Title }}</p>
The expected markup <p>Title 1</p> is shown.
It would appear that the directive has some sort of issue with either ng-repeat or that the data being used in the repeat is a subobject of the pageList object passed.
Furthermore, when the markup for the directive is just used in a regular page with data from the parent scope, there is no problem at all. It seems to be a problem with the directive itself.
EDIT
This is the page controller that is populating the data for the directive:
var pageEditController = function ($scope, $rootScope, $state, $stateParams, pageService) {
$scope.page = {};
$scope.errorMessage = "";
getPage(); // This is for other data on the page and not directly linked to directive issue.
getPages(); // This is to get the directive data
function getPage() { // Just an http get method for $scope.page }}
function getPages() {
pageService.getTree()
.success(function (result) {
$scope.pageList = result.Result; // This is where the directive data is loaded into scope
})
.error(function (error) {
$scope.errorMessage = 'Unable to load pages';
});
};
});
Further strange behaviour:
A template with this:
<p>{{ pageList.Title }}</p>
shows the Title OK.
This:
<p>{{ pageList.Title }}</p><p>{{ pageList.Id }}</p>
shows nothing at all. Obviously ng-repeat in original example doesn't work either.
In the directive, you have mentioned as "pageList". But in the template, you are accessing it using "PageList". So, I guess that it may solve using issue.
As we detect in comment: your code work OK, and problem with template for directive.
When you use replace:true in your directive, template that you load must have exactly one root element otherwise you get next error:
Error: [$compile:tplrt] Template for directive 'pageSelectList' must have exactly one root element. PageSelectList.html
https://docs.angularjs.org/error/$compile/tplrt?p0=pageSelectList&p1=PageSelectList.html
So, for solving you have two way:
1) wrap your template in container, like a div
<div>
your directive
<p>{{ pageList.Title }}</p><p>{{ pageList.Id }}</p>
</div>
2) remove flag replace, or use it replace: false.

AngularJS append html to dom element

Im working on a small web app, and there is a side menu that has nav links in it. Each link when clicked pulls out a hidden panel and should display a list of items specific to that link.
I have most of the functionality working except Im stuck on how to append either a templateURL or just html to the panel.
Any guidance would be great.
heres what I have so far:
html
<!-- Pullout menu -->
<nav id="sidebar-pullout">
<div id="menu-list"></div>
</nav>
app.js
var configApp = angular.module("configApp", ['ngRoute','ui.bootstrap'])
.config(function($routeProvider){
$routeProvider..when('/organizations', {
templateUrl: 'templates/dashboard/organizations/organizations-title.html',
controller: 'OrganizationController',
activetab: 'organizations'
})
.otherwise( {redirectTo: '/dashboard'} );
});
// Side Nav Link Controllers
configApp.controller('OrganizationController', function($scope) {});
configApp.controller('SideNavCtrl', function($scope, $location) {
$scope.isActive = function(route) {
return route === $location.path();
}
});
// adding html to the menu-list
configApp.directive('menu-list', function(){
return {
template: '<span ng-transclude >append som html here</span>',
replace: true,
transclude: true,
controller: 'OrganizationController'
};
});
Here is another way you might be able to go about it. By keeping a reference to menu items and contents. You could keep the side panel content in separate HTML files.
configApp.directive('menuList', function() {
return {
restrict: 'EA',
link: function(scope, el, attr) {
var activeId = null;
scope.showContent = function(id) {
activeId = id;
};
scope.isActive = function(id) {
return activeId === id;
}
scope.menuItems = [{
id: 'item1',
name: 'Menu Item 1',
content: 'path/to/menuItem1content.html'
}, {
id: 'item2',
name: 'Menu Item 2',
content: 'path/to/menuItem2content.html'
}]
}
};
});
Then in you HTML maybe something like this.
<div menuList>
<nav id="sidebar-menu">
<ul>
<li ng-repeat="item in menuItems">
<a ng-click="showContent(item.id)">{{ item.name }}</a>
</li>
</ul>
</nav>
<div id="sidebar-content">
<div class="content"
ng-repeat="item in menuItems"
ng-include="item.content"
ng-show="isActive(item.id)"></div>
</div>
</div>
This is just an idea and you could use angular animation to animate the sidebar sliding and stuff.
You are specifying your ng-transclude directive on the wrong element. You are placing it on your span tag. Try something like this instead:
<div>
<span>/*My template html here*/</span>
<div ng-transclude></div>
</div>
It also looks like you're specifying your directive incorrectly. Try this:
configApp.directive('menuList', function () {
return {
restrict: 'A',
replace: true, // note: this syntax will soon be deprecated
template: '<see above snippet>'
};
});
In particular, notice restrict, which specifies how this directive will be used (A: attribute on html element, E: as an element itself, C: as a class). Here we are saying we want to use our directive as an element, E. Also, note that I used the name menuList instead of menu-list. AngularJS uses camel-case in directive definition, and maps the directive names found in the html into their camel case counterparts. So, in the html we will still use this directive like this: menu-list, but we will declare it using camel-case.
Hope this helps!

Mimicking collection changes in ng-repeat

I have an AngularJS app. My app has the following JSON array defined on the scope when the controller is initialized:
$scope.orders= [
{ isSelected:false, quantity:0, price:1.33, name:'', description:'' },
{ isSelected:false, quantity:0, price:2.54, name:'', description:'' }
];
...
var addOrder = function() {
$scope.orders.push({ isSelected:false, quantity:0, price:getPrice(), name:getName(), description:getDescription() });
};
I'm displaying the orders like this:
<ul class="list">
<li ng-repeat="order in orders">
<div>
<h2>{{order.name}}</h2>
<p>{{order.description}}</p>
</div>
</li>
</ul>
Whenever addOrder gets called, a object gets added to the order collection. However, that change doesn't get reflected in the UI. How do I get the change to reflect in the UI?
Thanks
$scope.$apply() is required to update the scope. When you use Angular THINGS like directive, ng-* events, $timeout, $http, etc they come pre-wrapped with this function. When you work outside of Angular's world you'll need to start telling it when you update the scope.
Here's a good rundown on it all: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
Be careful about simply sticking it within generic functions - you'll run into problems (calling $apply within $apply) if they are called by Angular THINGS as well as vanilla JS events. Associate the $apply() with what requires it:
.directive( 'myDir', [ function() {
return {
link: function( scope ) {
( scope.myFunc = function() {
// scope.$apply(); would cause error
})();
angular.element( window ).bind( 'resize', function(){ scope.$apply( scope.myFunc )});
}
}
}]);

Dynamically insert directives using meta data in AngularJS?

I'd like to define meta data which will dynamically use the correct directive based on a "type" value:
$scope.items = [
{
type: 'directive-one',
value: 'One'
},{
type: 'directive-two',
value: 'Two'
},{
type: 'directive-three',
value: 'Three'
}
];
and then
<li ng-repeat="item in items" {{type}}>
{{value}}
</li>
I've created a jsfiddle here. So far I've had no success
Is this possible? How would I accomplish this?
Here is an alternative way of solving the problem:
Use ngSwitch to map between type and directive.
<li ng-repeat="item in items">
<div ng-switch on="item.type">
<div ng-switch-when="type-one" directive-one>
</div>
<div ng-switch-when="type-two" directive-two>
</div>
<div ng-switch-when="type-three" directive-three>
</div>
</div>
</li>
See jsfiddle
But if you really need to define the directive in the metadata, you can add a directive that will generate the div element with the appropriate directive
angular.module('myApp').directive('dynamicDirective', function($compile) {
return {
restrict: 'A',
link: function (scope, ele) {
//add a child div element that contains the directive specified in the type property
var itemEl = angular.element('<div>').attr(scope.item.type,'');
$compile(itemEl)(scope);
ele.append(itemEl);
}
};
});
See jsfiddle

Categories

Resources