Dom bindings not getting updated on ionic content on-scroll event - javascript

I want to do a progress bar when user scrolls down. When it's down all the way, the bar is 100% width.
http://codepen.io/hawkphil/pen/vOyPwN
HTML
Ionic List Directive
<link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
<script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
</head>
<body ng-controller="MyCtrl">
<ion-header-bar class="bar-positive">
<div class="buttons">
<button class="button button-icon icon ion-ios-minus-outline"
ng-click="data.showDelete = !data.showDelete; data.showReorder = false"></button>
</div>
<h1 class="title">Ionic Delete/Option Buttons</h1>
<div class="buttons">
<button class="button" ng-click="data.showDelete = false; data.showReorder = !data.showReorder">
Reorder
</button>
</div>
<!--div class="progress" ng-style='{"width": pos.x}'></div-->
<div class="progress" ng-style='getXObj()'></div>
</ion-header-bar>
<ion-content on-scroll="isScrolling()">
<!-- The list directive is great, but be sure to also checkout the collection repeat directive when scrolling through large lists -->
<ion-list show-delete="data.showDelete" show-reorder="data.showReorder">
<ion-item ng-repeat="item in items"
item="item"
href="#/item/{{item.id}}" class="item-remove-animate">
Item {{ item.id }}
<ion-delete-button class="ion-minus-circled"
ng-click="onItemDelete(item)">
</ion-delete-button>
<ion-option-button class="button-assertive"
ng-click="edit(item)">
Edit
</ion-option-button>
<ion-option-button class="button-calm"
ng-click="share(item)">
Share
</ion-option-button>
<ion-reorder-button class="ion-navicon" on-reorder="moveItem(item, $fromIndex, $toIndex)"></ion-reorder-button>
</ion-item>
</ion-list>
</ion-content>
</body>
</html>
JS
angular.module('ionicApp', ['ionic'])
.controller('MyCtrl', function($scope, $ionicScrollDelegate) {
$scope.data = {
showDelete: false
};
$scope.edit = function(item) {
alert('Edit Item: ' + item.id);
};
$scope.share = function(item) {
alert('Share Item: ' + item.id);
};
$scope.moveItem = function(item, fromIndex, toIndex) {
$scope.items.splice(fromIndex, 1);
$scope.items.splice(toIndex, 0, item);
};
$scope.onItemDelete = function(item) {
$scope.items.splice($scope.items.indexOf(item), 1);
};
$scope.items = [
{ id: 0 },
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
{ id: 8 },
{ id: 9 },
{ id: 10 },
{ id: 11 },
{ id: 12 },
{ id: 13 },
{ id: 14 },
{ id: 15 },
{ id: 16 },
{ id: 17 },
{ id: 18 },
{ id: 19 },
{ id: 20 },
{ id: 21 },
{ id: 22 },
{ id: 23 },
{ id: 24 },
{ id: 25 },
{ id: 26 },
{ id: 27 },
{ id: 28 },
{ id: 29 },
{ id: 30 },
{ id: 31 },
{ id: 32 },
{ id: 33 },
{ id: 34 },
{ id: 35 },
{ id: 36 },
{ id: 37 },
{ id: 38 },
{ id: 39 },
{ id: 40 },
{ id: 41 },
{ id: 42 },
{ id: 43 },
{ id: 44 },
{ id: 45 },
{ id: 46 },
{ id: 47 },
{ id: 48 },
{ id: 49 },
{ id: 50 }
];
$scope.pos = {x:'21.41982072480742%', xObj: {}};
$scope.isScrolling = function() {
console.log($ionicScrollDelegate.getScrollPosition());
$scope.pos.x = ($ionicScrollDelegate.getScrollPosition().top / 2204 * 100) + '%';
console.log($scope.pos.x);
//$scope.getXObj();
}
$scope.getXObj = function() {
console.log({"width": $scope.pos.x});
return {"width": $scope.pos.x + "%"};
}
});
CSS
body {
cursor: url('http://ionicframework.com/img/finger.png'), auto;
}
.progress {
height: 2px;
background: red;
}
I have tried two things:
ng-style='{"width": pos.x}'
and
ng-style='getXObj()'
But didn't bind to provide the red bar expanding while I scroll down.
I have read this question:
Angular.js ng-style won't bind value
I am running out of idea why although the console is showing the right percentage value.
Any clue why this doesn't work?

Seems like a possible ionic bug. It appears to call the function alright on the scroll event of the element but does not call digest cycle to update DOM.
Snippet from ionic:
var scrollFunc = function(e) {
var detail = (e.originalEvent || e).detail || {};
$scope.$onScroll && $scope.$onScroll({ //<-- Here it gets called but it is outside of angular context
event: e,
scrollTop: detail.scrollTop || 0,
scrollLeft: detail.scrollLeft || 0
});
};
$element.on('scroll', scrollFunc); //event registration
So as a possible fix you could do a scope.$evalAsync which would be probably safer option.
$scope.isScrolling = function() {
$scope.pos.x = ($ionicScrollDelegate.getScrollPosition().top / 2204 * 100) + '%';
$scope.$evalAsync(angular.noop);
}
Demo

Related

Vue JS error Parsing error: Unexpected token - Error is absolutely useless

I am trying to learn frontend framework Vue. Following this tutorial. This is my first time with JS and I only know basic javascript concepts. Searching for these issues, it seems like it has something to do with ESlint? I tried all configurations in the answers but nothing works.
My error:
88:20 error Parsing error: Unexpected token
49 | if (this.newEntryContent && this.selectedEntryType !== 'draft') {
50 | this.user.entries.unshift( items: {
> 51 | id: this.user.entries.length + 1,
| ^
52 | content: this.newEntryContent
53 | })
54 | }
✖ 1 problem (1 error, 0 warnings)
My template:
<form class="form" #submit.prevent="createNewEntry">
<label for="new-entry">New entry</label>
<textarea name="new-entry" rows="4" v-model="newEntryContent"></textarea>
<div class="entry-type">
<label for="entry-type">Type: </label>
<select id="entry-type" v-model="selectedEntryType">
<option :value="option.value" v-for="(option, index) in entryTypes" :key="index">
{{ option.name }}
</option>
</select>
</div>
<button>Post</button>
</form>
Here is my <script>.
<script>
import EntryItem from "#/components/EntryItem";
export default {
name: 'UserProfile',
components: { EntryItem },
data() {
return {
newEntryContent: '',
selectedEntryType: 'instant',
entryTypes: [
{value: 'draft', name: 'Draft'},
{value: 'instant', name: 'Instant'}
],
followers: 0,
user: {
id: 1,
username: 'john',
firstName: 'John',
lastName: 'Doe',
isAdmin: true,
entries: [
{id: 1, content: "My submission"},
{id: 2, content: "My awesome entry to another contest!"}
]
},
log: ""
}
},
watch: {
followers(newFollowercount, oldFollowercount) {
if (oldFollowercount < newFollowercount) {
this.log = oldFollowercount;
}
}
},
computed: {
fullName() {
return this.user.firstName + ' ' +this.user.lastName;
}
},
methods: {
followUser(){
this.followers += 1
},
toggleFavourite(id) {
console.log("You faved " + id);
},
createNewEntry() {
if (this.newEntryContent && this.selectedEntryType !== 'draft') {
this.user.entries.unshift( items: {
id: this.user.entries.length + 1,
content: this.newEntryContent
})
}
}
},
mounted() {
this.followUser();
}
}
</script>
The error is absolutely useless and it is not saying much about anything. There are no errors in the console too. How should I debug this and fix?

Angular js filter array inside array

I am having a hard time doing an Angular filter to solve a problem as below.
The filter logic is as below:
1) If all listItem of that item has qtyLeft != 0, do not display that item
2) If any of the listItem of that item has qtyLeft == 0, display the item title as well as coressponding listItem that have qtyLeft == 0
Here's a basic example of my data structure, an array of items:
$scope.jsonList = [
{
_id: '0001',
title: 'titleA',
list: {
listName: 'listNameA',
listItem: [
{
name: 'name1',
qtyLeft: 0
},
{
name: 'name2',
qtyLeft: 0
},
]
}
},
{
_id: '0002',
title: 'titleB',
list: {
listName: 'listNameB',
listItem: [
{
name: 'name3',
qtyLeft: 2
},
{
name: 'name4',
qtyLeft: 0
},
]
}
},
{
_id: '0003',
title: 'titleC',
list: {
listName: 'listNameC',
listItem: [
{
name: 'name5',
qtyLeft: 2
},
{
name: 'name6',
qtyLeft: 2
},
]
}
},
]
Here is the final expected outcome:
<div ng-repeat="item in jsonList | filter: filterLogic">
<div> </div>
</div>
// final outcome
<div>
<div>Title: titleA, ListItem: Name1, Name2</div>
<div>Title: titleB, ListItem: Name4</div>
</div>
Created working Plunkr here. https://plnkr.co/edit/SRMgyRIU7nuaybhX3oUC?p=preview
Do not forget to include underscore.js lib in your project if you are going to use this directive.
<div ng-repeat="jsonItem in jsonList | showZeroElement track by $index">
<div>Title:{{ jsonItem.title}}, ListItem:<span ng-repeat="item in
jsonItem.list.listItem track by $index" ng-if="item.qtyLeft==0">
{{item.name}}</span>
</div>
</div>
And
app.filter('showZeroElement', function() {
return function(input) {
var items = []
angular.forEach(input, function(value, index) {
angular.forEach(value.list.listItem, function(val, i) {
var found = _.findWhere(items, {
'title': value.title
})
if (val.qtyLeft === 0 && found === undefined) {
items.push(value)
}
})
})
return items
}
})

Knockout - Unable to process binding, gridViewModel is not defined

I am new to knockout and I have no idea why I am getting this message.
Unable to process binding "simpleGrid: function (){return gridViewModel }"
Message: gridViewModel is not defined;
library_customization.js
define(['services/logger'], function (logger) {
var title = 'Library Customization';
var vm = {
activate: activate,
title: title
};
return vm;
var initialData = [
{ name: "Well-Travelled Kitten", sales: 352, price: 75.95 },
{ name: "Speedy Coyote", sales: 89, price: 190.00 },
{ name: "Furious Lizard", sales: 152, price: 25.00 },
{ name: "Indifferent Monkey", sales: 1, price: 99.95 },
{ name: "Brooding Dragon", sales: 0, price: 6350 },
{ name: "Ingenious Tadpole", sales: 39450, price: 0.35 },
{ name: "Optimistic Snail", sales: 420, price: 1.50 }
];
var PagedGridModel = function (items) {
this.items = ko.observableArray(items);
this.addItem = function () {
this.items.push({ name: "New item", sales: 0, price: 100 });
};
this.sortByName = function () {
this.items.sort(function (a, b) {
return a.name < b.name ? -1 : 1;
});
};
this.jumpToFirstPage = function () {
this.gridViewModel.currentPageIndex(0);
};
this.gridViewModel = new ko.simpleGrid.viewModel({
data: this.items,
columns: [
{ headerText: "Item Name", rowText: "name" },
{ headerText: "Sales Count", rowText: "sales" },
{ headerText: "Price", rowText: function (item) { return "$" + item.price.toFixed(2) } }
],
pageSize: 4
});
};
ko.applyBindings(new PagedGridModel(initialData));
function activate() {
logger.log(title + ' selected', null, title, true);
return true;
}
});
library_customization.html
<!DOCTYPE html>
<html id="libraryCust">
<head>
<title> Project</title>
<script type="text/javascript" src="../../Scripts/knockout-3.3.0.js
</script>
</head>
<body>
<section>
<h2 class="page-title" data-bind="text: title"></h2>
</section>
<div class='liveExample'>
<div data-bind='simpleGrid: gridViewModel'></div>
<!-- -->
<button data-bind='click: addItem'>
Add item
</button>
<button data-bind='click: sortByName'>
Sort by name
</button>
<button data-bind='click: jumpToFirstPage, enable: gridViewModel.currentPageIndex'>
Jump to first page
</button>
</div>
</body>
</html>
Check the fiddle: JS Fiddle
I think the main problem is that you exit your module earlier than planned in the code below:
var title = 'Library Customization';
var vm = {
activate: activate,
title: title
};
return vm;
/* your main code follows below but never executes */
So I moved these properties into the PagedGridModel constructor.

Adding item to observablearray from a dropdown box using Knockout JS

I have two observablearrays in my ViewModel:
ShortlistedCountries
CompanyMainList
All companies names are displayed in a dropdown box. And the shortlisted companies are listed underneath it.
I would like to achieve two things from this demo.
Firstly, the users should be able to select the company name from the dropdown and add it to the Shortlisted company list.
Secondly, the users should get an error message (or alert) if they try to shortlist a company that has already been added to the shortlisted companies list.
Please have a look at my demo in JSFiddle
HTML
<div>
<div>All Companies</div>
<div>
<div id="rdoCompanyServer">
<select data-bind="options:CompanyMainList, optionsText:'CompanyName', optionsValue:'id', optionsCaption: 'Select a company...'"></select> Add to Shortlist
</div>
</div>
</div>
<br/>
<br/>
<br/>
<div>
<div id="sectionHeading">My Shortlisted Companies</div>
<div>
<div>
<ol data-bind="foreach: ShortlistedCountries">
<li><span data-bind="text:CompanyName"></span><span id="removeshortlist">
Remove</span>
</li>
</ol>
<br />
</div>
</div>
</div>
Knockout JS
function CompanyViewModel() {
var self = this;
self.currentDemoLicenses = ko.pureComputed(function () {
return self.demoLicenses().length;
});
/* adding bookmark servers in the same view TEST */
self.bookmarkedServerCount = ko.pureComputed(function () {
return self.ShortlistedCountries().length;
});
self.ShortlistedCountries = ko.observableArray([{
CompanyName: 'Apple Inc',
id: 11
}, {
CompanyName: 'TDK',
id: 15
}, {
CompanyName: 'Samsung',
id: 16
}
]);
self.DeleteShortlistedCountries = function (ShortlistedCountries) {
self.ShortlistedCountries.remove(ShortlistedCountries);
};
self.AddToShortlistedCountries = function () {
self.ShortlistedCountries.push(self.ShortlistedCountries);
};
self.CompanyMainList = ko.observableArray([{
CompanyName: 'Acer',
id: 1
}, {
CompanyName: 'Toshiba',
id: 12
}, {
CompanyName: 'Sony',
id: 13
}, {
CompanyName: 'LG',
id: 14
}, {
CompanyName: 'HP',
id: 6
}, {
CompanyName: 'Hitachi',
id: 6
}, {
CompanyName: 'Apple Inc',
id: 11
}, {
CompanyName: 'TDK',
id: 15
}, {
CompanyName: 'Samsung',
id: 16
}, {
CompanyName: 'Panasonic',
id: 7
}]);
};
$(document).ready(function () {
ko.applyBindings(new CompanyViewModel());
});
Have a look at my demo in JSFiddle
Please let me know if I am missing some thing or is there anything wrong with my code.
Thank you.
Kind regards.
Sid
Try something like this
ViewModel:
function CompanyViewModel() {
var self = this;
self.selectedCompany = ko.observable();//has dropdown selection
self.ShortlistedCompanies = ko.observableArray([{
CompanyName: 'Apple Inc',
id: 11
}, {
CompanyName: 'TDK',
id: 15
}, {
CompanyName: 'Samsung',
id: 16
}
]);
var isExists = function (data) { //function checks for duplicates
var status = false;
ko.utils.arrayFirst(self.ShortlistedCompanies(), function (item) {
if (item.id === data.id) {
status = true;
return status;
}
});
return status;
}
self.DeleteShortlistedCompanies = function (ShortlistedCompanies) {
self.ShortlistedCompanies.remove(ShortlistedCompanies);
};
self.AddToShortlistedCompanies = function () {
if (!self.selectedCompany()) {
alert('select something');
return;
}
if (isExists(self.selectedCompany())) {
alert('Cannot add duplicates');
return;
}
self.ShortlistedCompanies.push(self.selectedCompany());
};
self.CompanyMainList = ko.observableArray([{
CompanyName: 'Acer',
id: 1
}, {
CompanyName: 'Toshiba',
id: 12
}, {
CompanyName: 'Sony',
id: 13
}, {
CompanyName: 'LG',
id: 14
}, {
CompanyName: 'HP',
id: 6
}, {
CompanyName: 'Hitachi',
id: 6
}, {
CompanyName: 'Apple Inc',
id: 11
}, {
CompanyName: 'TDK',
id: 15
}, {
CompanyName: 'Samsung',
id: 16
}, {
CompanyName: 'Panasonic',
id: 7
}]);
};
View :
<div id="rdoCompanyServer">
<select data-bind="options:CompanyMainList, optionsText:'CompanyName', optionsCaption: 'Select a company...',value:selectedCompany"></select> Add to Shortlist
</div>
For reference working fiddle here

How do i correctly put a $scope.$watch on a two dimensional array?

Putting an Angular watch on a multidimensional array is proving problematic
I have a screen where the user will see two teams (outer array) with a teamsheet(inner array) for each team. He is able to drag and drop the players to change the batting order.
The battingOrder is updated according to the player's spot in the array.
The following solution is a bit of a cop out/short term, but it does solve my immediate stumbling block.
A more preferable solution would be appreciated though.
The following code is in my controller.
$scope.$watch(
"Teams[0].TeamSheet",
function (newValue, oldValue) {
for (var i = 0; i < newValue.length; i++) {
newValue[i].battingOrder = i + 1;
}
},
true
);
$scope.$watch(
"Teams[1].TeamSheet",
function (newValue, oldValue) {
for (var i = 0; i < newValue.length; i++) {
newValue[i].battingOrder = i + 1;
}
},
true
);
$scope.Teams = [{
TeamName: "South-Africa",
TeamSheet: [
{
name: "A Peterson",
battingOrder: 1,
mode: "view"
},
{
name: "Riley Rossouw",
battingOrder: 2,
mode: "view"
},
{
name: "H Amla",
battingOrder: 3,
mode: "view"
},
{
name: "F Du Plessis",
battingOrder: 4,
mode: "view"
},
{
name: "AB De Villiers",
battingOrder: 5,
mode: "view"
}
]
},
{
TeamName: "Australia",
TeamSheet: [
{
name: "D Warner",
battingOrder: 1,
mode: "view"
},
{
name: "S Watson",
battingOrder: 2,
mode: "view"
},
{
name: "M Clarke",
battingOrder: 3,
mode: "view"
},
{
name: "C Rogers",
battingOrder: 4,
mode: "view"
},
{
name: "S Smith",
battingOrder: 5,
mode: "view"
}
]
}];
you can try to update batting order directly inside the view:
<div ng-repeat="member in team" ng-init="member.battingOrder = $index">
</div>
then you do not need to use $watch
EDIT
the previous solution will not work after you swap items in your array. Use the following code to overcome this problem:
<body ng-controller="MainCtrl">
<div ng-repeat="member in team">
{{setIndex(member,$index)}}
{{member.name}},{{member.index}}
</div>
<button ng-click="swap()">swap first with last</button>
</body>
controller:
app.controller('MainCtrl', function($scope) {
$scope.team = [ { index: 0, name : 'first' }, { index: 0, name : 'second' }, { index: 0, name : 'third' } ];
$scope.swap = function () {
var tmp = $scope.team[0];
$scope.team[0] = $scope.team[2];
$scope.team[2] = tmp;
}
$scope.setIndex = function (member, index)
{
member.index = index;
}
});
http://plnkr.co/edit/V3ixkww7dxcx8uZ7f0hj?p=preview

Categories

Resources