Mixitup Angular.js directive (Angular.js watch doesn't work) - javascript

I need to integrate Angular.js with Mixitup, so I created directive like that
this is JSFiddle to my code: http://jsfiddle.net/zn7t9p6L/19/
var app = angular.module('app', []);
app.directive('mixitup',function(){
var linker = function(scope,element,attrs) {
scope.$watch('entities', function(){
console.log('reload');
element.mixItUp();
// how to tell mixitup to reload the data
});
console.log('starting')
};
return {
restrict:'A',
link: linker,
scope:{entities:'='}
}
})
app.controller('DrawingsController',
function DrawingsController($scope, $timeout) {
$scope.categories = ['Soft', 'Elements'];
$scope.drawings = [{
name: 'Water',
category: 'Elements',
value: '2'
}, {
name: 'Fire',
category: 'Elements',
value: '1'
}, {
name: 'Air',
category: 'Elements',
value: '4'
}, {
name: 'Coton',
category: 'Soft',
value: '3'
}, {
name: 'Whool',
category: 'Soft',
value: '5'
}];
$scope.add = function(){
$scope.drawings.push({name:'new soft',value:$scope.drawings.length,category:'Soft'})
console.dir($scope.drawings);
};
});
<script src="http://cdn.jsdelivr.net/jquery.mixitup/2.0.4/jquery.mixitup.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<div ng-controller="DrawingsController">
<div class="controls">
<label>Filter:</label>
<button class="filter" data-filter="all">All</button>
<button class="filter"
data-filter=".category-{{category}}"
ng-repeat="category in categories">{{category}}</button>
<label>Sort:</label>
<button class="sort" data-sort="myorder:asc">Asc</button>
<button class="sort" data-sort="myorder:desc">Desc</button>
<label>Add:</label>
<button data-ng-click="add()">Add</button>
</div>
<div mixitup='mixitup' class="container" entities='drawings'>
<div class="mix category-{{drawing.category}}"
data-myorder="{{drawing.value}}"
ng-repeat="drawing in drawings">Value : {{drawing.name}}</div>
</div>
</div>
My problem is when I try to add new element to the drawings array or even change the array, it doesn't reflect the changes immediately, you need to make some filters like sorting to reflect changes.
Also the watcher to "entities" work once at the beginning and doesn't work anymore when any changes happen later to the drawings array (it will print reload one time and will not print it anymore)
you can try it in jsfiddle

You can try to pass a 3rd argument to .$watch() as true.
http://docs.angularjs.org/api/ng.$rootScope.Scope
$watch(watchExpression, listener, objectEquality)
objectEquality(optional) – {boolean=} – Comparing the object for equality rather than for reference.

I found the answer after some digging
As #gayu said , the first step is to set the 3rd argument to be true this will fix the second problem of the watcher
To fix the first problem which is updating mixitup immediately , you will need to add this script to the watcher callback
element.mixItUp();
if(element.mixItUp('isLoaded')){
element.mixItUp('destroy', true);
element.mixItUp();
}
this the new JSfiddle : http://jsfiddle.net/zn7t9p6L/20/

Related

AngularJS - Checklist-Model doesn't update the model correctly on checkbox change

I'm new to AngularJS and I'm having a problem with the Checklist-Model directive.
I'm using one of their examples to replicate this behavior.
When I click one checkbox and call a function, the model seems to be updated
correctly and is shown accordingly on the template, but when I log the contents of the model on the console the value of the checkbox I clicked is missing.
Here's when the strange stuff starts. If I click the checkbox again, then the value is removed from the template but I can see it on the console.
Here's the code:
HTML:
<div ng-controller="DemoCtrl">
<label ng-repeat="role in roles">
<input type="checkbox" checklist-model="user.roles"
checklist-value="role.text"
ng-change="changeValues(role)"> {{role.text}}
</label>
<br>
<button ng-click="checkAll()">check all</button>
<button ng-click="uncheckAll()">uncheck all</button>
<button ng-click="checkFirst()">check first</button>
<br><br>
user.roles {{ user.roles | json }}
</div>
Angular:
angular.module("DemoApp", ["checklist-model"])
.controller('DemoCtrl', function($scope) {
$scope.roles = [
{id: 1, text: 'guest'},
{id: 2, text: 'user'},
{id: 3, text: 'customer'},
{id: 4, text: 'admin'}
];
$scope.user = {
roles: ['guest', 'admin']
};
$scope.checkAll = function() {
$scope.user.roles = $scope.roles.map(function(item) { return item.id; });
};
$scope.uncheckAll = function() {
$scope.user.roles = [];
};
$scope.checkFirst = function() {
$scope.user.roles.splice(0, $scope.user.roles.length);
$scope.user.roles.push(1);
};
$scope.changeValues = function() {
console.log('Roles: ', JSON.stringify($scope.user.roles));
}
});
The first time I click a checkbox i.e: 'User' the output on the console is:
Roles: ["guest","admin"]
Then, when I uncheck the same checkbox the output is:
Roles: ["guest","admin","user"]
In the application I'm developing I MUST call a function when the checkbox changes it's value that's why I using the "ng-change" directive.
Can anybody explain what I'm doing wrong?
Thanks in advance.
checklist-model it's used the Id as checklist-value in the checkbox and in your code you use the text.
So in the function $scope.checkAll you should return the item.text instead item.id and in the function $scope.checkFirst you should push the $scope.roles.find(item => item.id == 1).text
And the rest seems correct

Strange behaviour Angular driven select list

According to paper "How to set the initial selected value of a select element using Angular.JS ng-options & track by" by #Meligy which I used as a guidance to learn and solve my problem with implementing a select list (ng-options), I still encounter some strange collaterale behaviour.
Although the basic behaviour finally does what it should do, see Test Plunk, I still encounter strange behaviour on the selected item in that list. Not in my test plunk though, implemented in my developement site.
app.controller("TaskEditCtrl", function($scope) {
$scope.loadTaskEdit = loadTaskEdit;
function loadTaskEdit() {
taskLoadCompleted();
tasktypesLoadCompleted();
}
function taskLoadCompleted() {
$scope.tasks = [{
Id: 1,
Name: "Name",
Description: "Description",
TaskTypesId: 4
}
];
$scope.current_task_tasktypesid = $scope.tasks[0].TaskTypesId;
}
function tasktypesLoadCompleted() {
var tasktypes = [{ Id: 1, Name: "A" },
{ Id: 2, Name: "B" },
{ Id: 3, Name: "C" },
{ Id: 4, Name: "D" }];
$scope.available_tasktypes_models = tasktypes
}
$scope.submit = function(){
alert('Edited TaskViewModel (New Selected TaskTypeId) > Ready for Update: ' + $scope.tasks[0].TaskTypesId);
}
loadTaskEdit();
});
HTML
<form class="form-horizontal" role="form" novalidate angular-validator name="editTaskForm" angular-validator-submit="UpdateTask()">
<div ng-repeat="task in tasks">
<div>
<select ng-init="task.TaskTypes = {Id: task.TaskTypesId}"
ng-model="task.TaskTypes"
ng-change="task.TaskTypesId = task.TaskTypes.Id"
ng-options="option_tasttypes.Name for option_tasttypes in available_tasktypes_models track by option_tasttypes.Id">
</select>
</div>
</div>
<div class="">
<input type="submit" class="btn btn-primary" value="Update" ng-click="submit()" />
</div>
</form>
As said, see my test plunk which shows exactly what it supposed to do. Moreover, using 5 self-explaining images, I do hope to make my troulbe bit clearer what's the problem.
I'm a bit lost to figure out what's so troublesome. My 'water' is telling me something wrong or missing in css. Did have anybody out their ever have face comparable? What could cause me this trouble? Does have anybody out there have a clue?
Thanks in advance
[1
[]2
[]3
[]4
Apparently I'm a rookie on css. Any suggestion is welcome!
CSS
#region "style sheets"
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/css/site.css",
"~/content/css/bootstrap.css",
"~/content/css/bootstrap-theme.css",
"~/Content/css/font-awesome.css",
"~/Content/css/morris.css",
"~/Content/css/toastr.css",
"~/Content/css/jquery.fancybox.css",
"~/Content/css/loading-bar.css"));
#endregion "style sheets"
The key with the dropdown is to set the model to the object that was selected. I updated your code to behave the way that I believe you are asking for it to work.
The key differences are:
Set the ng-model of the dropdown to the selected object and not the id of the selected item. This will give you access to the full selected object and all it's properties.
Remove the ng-change binding - this is not necessary with 2 way data binding, and the value on the model (whatever is put in for ng-model) will automatically be updated.
In your HTML you were using properties that were never declared in the Controller $scope. I updated those to reflect the available variables that were in scope.
For more information on dropdowns please see the angular documentation. It's very useful for figuring these types of issues out - https://docs.angularjs.org/api/ng/directive/select
// Code goes here
var app = angular.module("myApp", []);
app.controller("TaskEditCtrl", function($scope) {
$scope.tasks = {};
$scope.current_task_tasktypesid = null;
$scope.selected_task_tasktype = null;
$scope.loadTaskEdit = loadTaskEdit;
function loadTaskEdit() {
taskLoadCompleted();
tasktypesLoadCompleted();
//EDIT: DEFAULT DROPDOWN SELECTED VALUE
$scope.selected_task_tasktype = $scope.available_tasktypes_models[2];
}
function taskLoadCompleted() {
$scope.tasks = [{
Id: 1,
Name: "Name",
Description: "Description",
TaskTypesId: 4
}
];
$scope.current_task_tasktypesid = $scope.tasks[0].TaskTypesId;
}
function tasktypesLoadCompleted() {
var tasktypes = [{ Id: 1, Name: "A" },
{ Id: 2, Name: "B" },
{ Id: 3, Name: "C" },
{ Id: 4, Name: "D" }];
$scope.available_tasktypes_models = tasktypes
}
$scope.submit = function(){
alert('submitted model: ' + $scope.selected_task_tasktype.Id);
}
loadTaskEdit();
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.2.9" src="http://code.angularjs.org/1.2.9/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp" ng-controller="TaskEditCtrl as edit">
<form class="form-horizontal" role="form" novalidate angular-validator name="editTaskForm" angular-validator-submit="UpdateTask()">
<div ng-repeat="task in available_tasktypes_models">
<div>Task (Id): {{task.Id}}</div>
<div>Name: {{task.Name}}</div>
<div>Descripton: {{task.Description}}</div>
</div>
<p>Current Task.TaskTypesId: {{selected_task_tasktype.Id}}</p>
<div>
<select
ng-model="selected_task_tasktype"
ng-options="option_tasttypes.Name for option_tasttypes in available_tasktypes_models track by option_tasttypes.Id">
</select>
</div>
<p>{{task.TaskTypes}}</p>
<p>{{selected_task_tasktypesid = task.TaskTypes}}</p>
<div class="">
<input type="submit" class="btn btn-primary" value="Update" ng-click="submit()" />
</div>
</form>
</body>
</html>
First, I need to state the implementation of #Meligy and the suggested input of 'dball' are correct. So, go with the flow of your choice.
Keep notice on your style sheets.
Finally, I figured out that the style property 'color' with the value 'white' of selector #editTaskWrapper as identifier of the parent
<div id="editTaskWrapper">
acted as the bad guy. One way or the other, if I comment 'color: white' in
#editTaskWrapper {
background-color: #337AB7;
/*color: white;*/
padding: 20px;
}
the selected item in the selectlist becomes visible. All other controls and values are not affected, only the selected list item.

Dynamically access an array using ng-repeat

I need to access different arrays based on the users choice and then run through the array with a ng-repeat.
Controller:
$scope.allbooks={
book1:{price:"3.00",type:"non-fiction",chapters:book1chapters},
book2:{price:"4.00",type:"fiction",chapters:book2chapters},
};
$scope.pick = function(selectedBook) {
$rootScope.choice = selectedBook;
}
$scope.book1chapters=[
{title:"it begins"},
{title:"another one"}
];
$scope.book2chapters=[
{title:"hello"},
{title:"calling from the otherside"}
];
HTML:
<button ng-click="pick(allbooks.book1)">Book 1</button>
<button ng-click="pick(allbooks.book2)">Book 2</button>
<div ng-repeat:"m in choice.chapters"><-----this does not work
Chapter: {{m.title}}
</div>
This is a very simplified example just to make it easier to look at :) I don't know how t reference another array from inside an array. Thanks
It seems you did not define book1chapters and book1chapters for collection allbooks, instead you defined them in $scope which is not correct. Also change $rootScope to $scope since rootScope is not injected. The following code is working:
var book1chapters = [{
title: "it begins"
}, {
title: "another one"
}];
var book2chapters = [{
title: "hello"
}, {
title: "calling from the otherside"
}];
$scope.allbooks = {
book1: {
price: "3.00",
type: "non-fiction",
chapters: book1chapters
},
book2: {
price: "4.00",
type: "fiction",
chapters: book2chapters
},
};
$scope.pick = function(selectedBook) {
$scope.choice = selectedBook;
}
The code on plunker: http://plnkr.co/edit/83Ujp4R8BjIe39ROmp6n?p=preview
Short Answer
Basically you have incorrect ng-repeat syntax. It should have = instead of : before writing expression in front of ng-repeat directive like we do for value attribute
Markup
<div ng-repeat="m in choice.chapters"><-----this does not work
Chapter: {{m.title}}
</div>
Suggestions
You should not pollute $rootScope for sharing variables. For that you could create a shareable service which can share a data among-est various components of your app like controllers, directives, service, etc.
HTML
<button ng-click="sharableData.choice = 'book1'">Book 1</button>
<button ng-click="sharableData.choice = 'book2'">Book 2</button>
<div ng-repeat = "m in allbooks[sharableData.choice].chapters">
Chapter: {{m.title}}
</div>
Service
app.service('sharableData', function(){
var sharableData = this;
sharableData.sharedData = {
choice: undefined
};
});
Controller
app.controller('myCtrl', function($scope, sharableData){
//you other controller code
//add this additional line to expose service variable on html
$scope.sharableData = sharableData;
});

sharing state between different controllers in angular

I have two controls : Left Side Navigation and the right pane that changes the content on clicking of any item on left navigation.
Here is the html (angular view):
<nav class="navigation">
<ul class="list-unstyled" ng-controller="NavigationController as navigation">
<li ng-repeat="nav in navigation.tabs" class="has-submenu">
{{nav.name}}
<ul class="list-unstyled" ng-show="nav.subNav">
<li ng-repeat="subnav in nav.subNav">{{subnav.name}}</li>
</ul>
</li>
</ul>
</nav>
<section class="content" ng-controller="ContentSwitcher as content">
{{content.tab}}
<div class="warper container-fluid" >
<div class="container-scroll"></div>
</div>
</section>
And here is the controller
(function () {
var app = angular.module('provisioning', []);
app.service('contentService',function(){
var tab = 'Dashboard';
return {
getTab : function(){ return tab; },
setTab : function(value){ tab = value}
}
});
app.controller('NavigationController',['contentService','$log', function(cs,log){
this.tabs = [
{
name: 'Dashboard'
},
{
name: 'Manage',
subNav: [
{
name: 'Account'
},
{
name: 'Facility'
},
{
name: 'Doctors'
},
{
name: 'Patients'
},
{
name: 'Nurses'
},
{
name: 'Device Inventory'
}
]
},
{
name: 'Health Tracker'
},
{
name: 'Reports'
},
{
name: 'Settings'
},
{
name: 'Logout'
}
];
var template = this;
this.changeContent = function(tab){
cs.setTab(tab);
}
}]);
app.controller('ContentSwitcher', ['contentService',function(cs){
this.tab = cs.getTab();
}]);
})();
Also, is it best way to achieve what I intend to do in angularjs? I created a service and shared the variable in the two different controllers. However it doesn't work. The content on right never gets updated on clicking any of the item on left menu.
My answer to a previous question may help. It uses a type of observer pattern.
AngularJs update directive after a call to service method
Your service would change to allow all interested controller or directives to either generate or listen for certain events and access the associated data.
You need to tell your controller that the value of the tab has changed and then update it with the new value from the service. The second controller (ContentSwitcher) can not know, that the value changed and you simply assigned the string value of getTab once to this.tab.
One way to archive this automatically is changing the type of the variable inside your serivce from string to an object (e.g. tab = {name:'Dashboard'}) and then call $scope.$apply after you made changes to it.
In your first controller you still assign this.tab = service.getTab() (which will be an object) and in your view {{tab.name}}.

Angular: how to hide DOM element based on the number of filtered elements in model?

Here's a simple controller that contains a list of users:
<script type="text/javascript">
angular.module('project', [])
.controller('UsersController', ['$scope', function ($scope) {
$scope.users = [
{ text: 'User 1', done: true, extension: 123 },
{ text: 'Another user', done: false, extension: 456 }];
}]);
$scope.selectedUsers = function () {
var results = [];
for (i = 0; i < $scope.users.length; i++) {
if ($scope.users[i].done) {
results.push($scope.users[i]);
}
}
return results;
};
</script>
And a simple HTML that just shows a checkbox for each user, and a message to select something if no user is selected.
<div ng-app="project" ng-controller="UsersController">
<input type="checkbox" ng-repeat="user in users" ng-model="user.done" id="selectUser{{$index}}" />
<div ng-hide="selectedUsers().length">Select something</div>
</div>
The code works, but it looks kind of ugly.
Is there a less procedural way of getting to the same result?
You can simply filter down the set of users based on the done property and check the length of it.
<div ng-hide="(users | filter:{done:true}).length">Select something</div>
angular.module('project', [])
.controller('UsersController', ['$scope', function ($scope) {
$scope.users = [
{ text: 'User 1', done: true, extension: 123 },
{ text: 'Another user', done: false, extension: 456 }];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="project" ng-controller="UsersController">
<input type="checkbox" ng-repeat="user in users" ng-model="user.done" id="selectUser{{$index}}" />
<div ng-hide="(users | filter:{done:true}).length">Select something</div>
</div>
The way you have it implemented is rather inefficient as the selectedUsers function, which iterates over all users, runs on every digest. It might be acceptable if you a small number of users.
For completeness sake, however, another approach would be to calculate the number of user.done === true and keep track of the count (rather than re-count):
$scope.users = [
{ text: 'User 1', done: true, extension: 123 },
{ text: 'Another user', done: false, extension: 456 }];
$scope.selection = {count: 0};
for (var i=0; i<$scope.users.length; i++){
if ($scope.users[i].done) $scope.selection.count++;
}
And amend selection.count on each ng-change of selection:
<div ng-app="project" ng-controller="UsersController">
<input type="checkbox" ng-repeat="user in users"
ng-model="user.done"
ng-change="selection.count = selection.count + user.done * 2 - 1">
<div ng-hide="selection.count">Select something</div>
</div>
(Note, that it had I had to use an object selection that holds that property count; just using $scope.selectionCount would not have worked due to how prototypical inheritance works and the fact that ng-repeat creates a child scope)

Categories