Here is the code
Controller.js
$scope.addFavorite = function (index) {
$scope.temp = {
id: index
};
$scope.dish = $localStorage.getObject('favorites', '{}');
console.log($localStorage.get('favorites'));
$localStorage.storeObject('favorites', JSON.stringify($scope.temp));
var favorites = $localStorage.getObject('favorites');
console.log(favorites);
favoriteFactory.addToFavorites(index);
$ionicListDelegate.closeOptionButtons();
}
Service.js
.factory('favoriteFactory', ['$resource', 'baseURL', function ($resource, baseURL) {
var favFac = {};
var favorites = [];
favFac.addToFavorites = function (index) {
for (var i = 0; i < favorites.length; i++) {
if (favorites[i].id == index)
return;
}
favorites.push({id: index});
};
favFac.deleteFromFavorites = function (index) {
for (var i = 0; i < favorites.length; i++) {
if (favorites[i].id == index) {
favorites.splice(i, 1);
}
}
}
favFac.getFavorites = function () {
return favorites;
};
return favFac;
}])
.factory('$localStorage', ['$window', function($window) {
return {
store: function(key, value) {
$window.localStorage[key] = value;
},
get: function(key, defaultValue) {
return $window.localStorage[key] || defaultValue;
},
storeObject: function(key, value) {
$window.localStorage[key] = JSON.stringify(value);
},
getObject: function(key,defaultValue) {
return JSON.parse($window.localStorage[key] || defaultValue);
}
}
}])
I want to make a Favorites function, and I want to put every item's ID that marked into an array.
But, it couldn't expand the array and only change the value.
Did I make something wrong on here? Or maybe I put a wrong method on here?
Thank you in advance!
I just create logic for storing object, you have to made logic for remove object from localstorage.
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS</title>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script>document.write('<base href="' + document.location + '" />');</script>
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9"></script>
</head>
<body ng-controller="MainCtrl">
<div ng-repeat="item in items">
{{item.item_name}}
<button ng-click="addFavorite(item.id)">Add to Favorite</button>
<br><hr>
</div>
</body>
</html>
<script type="text/javascript">
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$http)
{
$scope.items = [
{id:1,item_name:'Apple'},
{id:2,item_name:'Banana'},
{id:3,item_name:'Grapes'},
]
$scope.addFavorite = function (index)
{
if(localStorage.getItem('favorites')!=undefined)
{
var old_favorite = JSON.parse(localStorage.getItem('favorites'));
var obj = {index:index};
old_favorite.push(obj);
localStorage.setItem('favorites',JSON.stringify(old_favorite));
}
else
{
var obj = [{index:index}];
localStorage.setItem('favorites',JSON.stringify(obj));
}
}
});
</script>
Related
I'm trying to populate a select using React js, I'm using the example given on the react js docs(https://facebook.github.io/react/tips/initial-ajax.html) , which uses jquery to manage the ajax calling, I'm not able to make it work, so far i have this:
here the codepen : http://codepen.io/parlop/pen/jrXOWB
//json file called from source : [{"companycase_id":"CTSPROD","name":"CTS-Production"},{"companyc ase_id":"CTSTESTING","name":"CTS-Testing"}]
//using jquery to make a ajax call
var App = React.createClass({
getInitialState: function() {
return {
opts:[]
};
},
componentDidMount: function() {
var source="https://api.myjson.com/bins/3dbn8";
this.serverRequest = $.get(source, function (result) {
var arrTen = result[''];
for (var k = 0; k < ten.length; k++) {
arrTen.push(<option key={opts[k]} value={ten[k].companycase_id}> {ten[k].name} </option>);
}
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
return (
<div>
<select id='select1'>
{this.state.opts}
</select>
</div>
);
}
});
ReactDOM.render(
<App />,
document.getElementById('root')
);
html
<div id="root"></div>
any idea how to make it works, thanks.
You need to call setState to actually update your view. Here's a workable version.
//json file called from source : [{"companycase_id":"CTSPROD","name":"CTS-Production"},{"companyc ase_id":"CTSTESTING","name":"CTS-Testing"}]
//using jquery to make a ajax call
var App = React.createClass({
getInitialState: function() {
return {
opts:[]
};
},
componentDidMount: function() {
var source="https://api.myjson.com/bins/3dbn8";
this.serverRequest = $.get(source, function (result) {
var arrTen = [];
for (var k = 0; k < result.length; k++) {
arrTen.push(<option key={result[k].companycase_id} value={result[k].companycase_id}> {result[k].name} </option>);
}
this.setState({
opts: arrTen
});
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
return (
<div>
<select id='select1'>
{this.state.opts}
</select>
</div>
);
}
});
ReactDOM.render(
<App />,
document.getElementById('root')
);
I want to have a reload function but I found some difficulties.
My app should clear all data by using the reload function and grab the feed again. Event that works, but it shows me only the first 5 news (limit of my api/per page) and ignores completely the loadMore function.
factory
.factory('newsDataService', function($http) {
return {
GetPosts: function(page) {
return $http.get("http://newsapi.domain.tdl/");
},
GetMorePosts: function(page) {
return $http.get("http://newsapi.domain.tdl/?page=" + page);
}
};
})
controller
.controller('NewsCtrl', function($scope, newsDataService) {
$scope.page = 1;
$scope.noMoreItemsAvailable = false;
newsDataService.GetPosts().then(function(items){
$scope.items = [];
$scope.items = items.data.response;
});
$scope.Reload = function() {
console.log('reload');
newsDataService.GetPosts().then(function(items){
console.log(items);
$scope.items = items.data.response ;
$scope.noMoreItemsAvailable = false;
$scope.$broadcast('scroll.refreshComplete');
})
};
$scope.loadMore = function(argument) {
$scope.page++;
newsDataService.GetMorePosts($scope.page).then(function(items){
if (items.data.response) {
$scope.items = $scope.items.concat(items.data.response);
$scope.noMoreItemsAvailable = false;
} else {
$scope.noMoreItemsAvailable = true;
}
}).finally(function() {
$scope.$broadcast("scroll.infiniteScrollComplete");
});
};
})
template:
<ion-view view-title="News">
<ion-content>
<ion-refresher on-refresh="Reload()"></ion-refresher>
<div class="list">
<a collection-repeat="news in items" href="#/app/newsreader/{{news.id}}" class="item item-thumbnail-left">
<h2>{{news.headline}}</h2>
<div class="item-text-wrap" ng-bind-html="news.teaser"></div>
</a>
</div>
<ion-infinite-scroll ng-if="!noMoreItemsAvailable" on-infinite="loadMore()" distance="1%"></ion-infinite-scroll>
</ion-content>
</ion-view>
How can I resolve this?
Just found the solution on my own.
.controller('NewsCtrl', function($scope, newsDataService) {
$scope.items =[]
$scope.page = 1;
newsDataService.GetPosts().then(function(items){
$scope.items = items.data.response;
});
$scope.Reload = function() {
console.log('reload');
$scope.items =[];
$scope.page = 1;
$scope.loadMore();
};
$scope.loadMore = function(argument) {
$scope.page++;
newsDataService.GetMorePosts($scope.page).then(function(items){
if (items.data.response) {
$scope.items = $scope.items.concat(items.data.response);
$scope.noMoreItemsAvailable = false;
} else {
$scope.noMoreItemsAvailable = true;
}
}).finally(function() {
$scope.$broadcast("scroll.infiniteScrollComplete");
$scope.$broadcast('scroll.refreshComplete');
});
};
})
My database return a field with raw html string. I'd like to modify element by adding ng-click='showImage()'. For example, the original html is:
<p>
<img src="http://mywebsite.com/img-1.jpg" />
</p>
My source code in my controller:
var parser = new DOMParser();
var doc = parser.parseFromString($scope.body, 'text/html');
var images = doc.getElementsByTagName('img')
for (var i = 0; i < images.length; i++ ) {
var img = images[i];
img.setAttribute('ng-click','showImage()');
$scope.addImage(img.getAttribute('src'));
}
$scope.body = doc.documentElement.innerHTML;
The result is correct:
<p>
<img src="http://mywebsite.com/img-1.jpg" ng-click="showImage()"/>
</p>
But I got the following error message when I click the image:
Uncaught TypeError: scope.$apply is not a function
Why? Can anybody help? Thanks.
Edit (complete source code for the controller):
(function() {
'use strict';
angular
.module('App')
.controller('DetailsController', DetailsController);
DetailsController.$inject = ['$scope','$stateParams','ParseSvc','$controller'];
function DetailsController($scope, $stateParams, ParseSvc, $controller) {
$controller('BaseController', { $scope: $scope });
$scope.article = JSON.parse($stateParams.article);
activate();
function activate() {
var parser = new DOMParser();
var doc = parser.parseFromString($scope.article.body, 'text/html');
var images = doc.getElementsByTagName('img')
for (var i = 0; i < images.length; i++ ) {
var img = images[i];
img.setAttribute('ng-click','showImages(' + i + ')');
$scope.addImage(img.getAttribute('src'));
}
$scope.article.body = doc.documentElement.innerHTML;
}
}
})();
Base controller:
(function() {
'use strict';
angular.module('App')
.controller('BaseController', ['$scope','$ionicModal','$ionicSlideBoxDelegate','$ionicScrollDelegate',
function($scope, $ionicModal, $ionicSlideBoxDelegate, $ionicScrollDelegate) {
$scope.allImages = [];
$scope.zoomMin = 1;
$scope.addImage = function(src) {
$scope.allImages.push({'src' : src});
}
$scope.showImages = function(index) {
$scope.activeSlide = index;
$scope.showModal('templates/gallery-zoomview.html');
}
$scope.showModal = function(templateUrl) {
$ionicModal.fromTemplateUrl(templateUrl, {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal;
$scope.modal.show();
});
};
// Close the modal
$scope.closeModal = function() {
$scope.modal.hide();
$scope.modal.remove();
};
$scope.updateSlideStatus = function(slide) {
var zoomFactor = $ionicScrollDelegate.$getByHandle('scrollHandle' + slide).getScrollPosition().zoom;
if (zoomFactor == $scope.zoomMin) {
$ionicSlideBoxDelegate.enableSlide(true);
} else {
$ionicSlideBoxDelegate.enableSlide(false);
}
}
}]);
})();
I found an compatibility issue using the two mentioned components on same list, my html and js code below:
HTML:
<ion-content ng-controller="homeCtrl">
<ion-refresher on-refresh="loadNewContent()" pulling-text="LoadMore..." spinner="android"></ion-refresher>
<div ng-repeat="item in items">
<img ng-src="{{pathUrl+item['path']}}" style="height: auto;width:100%;">
</div>
<ion-infinite-scroll ng-if="hasMore" on-infinite="loadMoreContent()" spinner="spiral" distance="5" immediate-check="false"></ion-infinite-scroll>
</ion-content>
JavaScript:
JiCtrls.controller('homeCtrl', ['$scope', '$timeout', 'DbService', 'JsonService',
function ($scope, $timeout, DbService, JsonService) {
$scope.items = [];
$scope.hasMore = true;
var run = false;
loadData(0);
//下拉更新
$scope.loadNewContent = function () {
loadData(2);
// Stop the ion-refresher from spinning
$scope.$broadcast("scroll.refreshComplete");
};
//上拉更新
$scope.loadMoreContent = function () {
loadData(1);
$scope.$broadcast('scroll.infiniteScrollComplete');
};
function loadData(stateType) {
if (!run) {
run = true;
if ($scope.sql == undefined) {
$scope.sql = "select top 5 * from information ";
}
DbService.getData($scope.sql, '').success(function (data, status, headers, config) {
var convertData = JsonService.convertData(data);
if (stateType == 1 || stateType == 0) {
// $scope.items = $scope.items.concat(convertData);
for (var i = 0; i < convertData.length; i++) {
$scope.items.push(convertData[i]);
}
}
else {
for (var i = 0; i < convertData.length; i++) {
$scope.items.unshift(convertData[i]);
}
}
if (convertData == null || convertData.length <= 0) {
$scope.hasmore = false;
;
}
$timeout(function () {
run = false;
}, 500);
}).error(function (errorData, errorStatus, errorHeaders, errorConfig) {
console.log(errorData);
});
}
}
}
]);
Everything is normal in Chrome browser and Iphone, but in a part of the Android phone there is a big problem.When ion-refresher trigger on-refresh function,the on-infinite="loadMoreContent()" function will run indefinitely. So,What is the problem?
Try to put $scope.$broadcast("scroll.refreshComplete"); and $scope.$broadcast('scroll.infiniteScrollComplete'); into DbService.getData(...).success() callback, not in functions triggered by on-infinite and on-refresh.
Explanation:
When the user scrolls to the end of the screen, $scope.loadMoreContent which is registered with on-infinite is triggered. The spinner shows, and ion-infinite-scroll pauses checking whether the user has reached the end of the screen, until $scope.$broadcast('scroll.infiniteScrollComplete'); is broadcast, when it hides the spinner and resumes the checking.
In your code, imagine there's a 3s delay in network, no new item is added to the list in that 3s. As a result, the inner height of ion-content is never updated, so the check that determines whether the user has reached the end of the screen will always return true. And you effectively prevent ion-infinite-scroll from pausing this checking by broadcasting scroll.infiniteScrollComplete the moment on-infinite is triggered. That's why it will update indefinitely.
To improve your code quality and prevent future problems, you may need to call $scope.$apply in your DbService.getData().success() callback (depending on the implementation of getData) and manually notify ion-content to resize in the callback.
P.S. 来自中国的Ionic开发者你好 :-) 两个中国人讲英语真累啊
Update
I've made a codepen that combines both ion-refresher and ion-infinite-scroll, I think it's working pretty fine.
http://codepen.io/KevinWang15/pen/xVQLPP
HTML
<html ng-app="ionicApp">
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>ion-refresher + ion-infinite-scroll 2</title>
<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">
<h1 class="title">ion-refresher + ion-infinite-scroll 2</h1>
</ion-header-bar>
<ion-content delegate-handle="mainScroll">
<ion-refresher on-refresh="doRefresh()">
</ion-refresher>
<ion-list>
<ion-item ng-repeat="item in list">{{item}}</ion-item>
</ion-list>
<ion-infinite-scroll
ng-if="hasMore"
on-infinite="loadMore()"
distance="1%">
</ion-infinite-scroll>
</ion-content>
</body>
</html>
JS
angular.module('ionicApp', ['ionic'])
.controller('MyCtrl', function($scope, $timeout, $q, $ionicScrollDelegate) {
/*
list of items, used by ng-repeat
*/
$scope.list = [];
var itemOffset = 0,
itemsPerPage = 5;
/*
used by ng-if on ion-infinite-scroll
*/
$scope.hasMore = true;
/*
isRefreshing flag.
When set to true, on data arrive
it first empties the list
then appends new data to the list.
*/
var isRefreshing = false;
/*
introduce a custom dataFetcher instance
so that the old fetch process can be aborted
when the user refreshes the page.
*/
var dataFetcher=null;
/*
returns a "dataFetcher" object
with a promise and an abort() method
when abort() is called, the promise will be rejected.
*/
function fetchData(itemOffset, itemsPerPage) {
var list = [];
//isAborted flag
var isAborted = false;
var deferred = $q.defer();
//simulate async response
$timeout(function() {
if (!isAborted) {
//if not aborted
//assume there are 22 items in all
for (var i = itemOffset; i < itemOffset + itemsPerPage && i < 22; i++) {
list.push("Item " + (i + 1) + "/22");
}
deferred.resolve(list);
} else {
//when aborted, reject, and don't append the out-dated new data to the list
deferred.reject();
}
}, 1000);
return {
promise: deferred.promise,
abort: function() {
//set isAborted flag to true so that the promise will be rejected, and no out-dated data will be appended to the list
isAborted = true;
}
};
}
$scope.doRefresh = function() {
//resets the flags and counters.
$scope.hasMore = true;
itemOffset = 0;
isRefreshing = true;
//aborts previous data fetcher
if(!!dataFetcher) dataFetcher.abort();
//triggers loadMore()
$scope.loadMore();
}
$scope.loadMore = function() {
//aborts previous data fetcher
if(!!dataFetcher) dataFetcher.abort();
//fetch new data
dataFetcher=fetchData(itemOffset, itemsPerPage);
dataFetcher.promise.then(function(list) {
if (isRefreshing) {
//clear isRefreshing flag
isRefreshing = false;
//empty the list (delete old data) before appending new data to the end of the list.
$scope.list.splice(0);
//hide the spin
$scope.$broadcast('scroll.refreshComplete');
}
//Check whether it has reached the end
if (list.length < itemsPerPage) $scope.hasMore = false;
//append new data to the list
$scope.list = $scope.list.concat(list);
//hides the spin
$scope.$broadcast('scroll.infiniteScrollComplete');
//notify ion-content to resize after inner height has changed.
//so that it will trigger infinite scroll again if needed.
$timeout(function(){
$ionicScrollDelegate.$getByHandle('mainScroll').resize();
});
});
//update itemOffset
itemOffset += itemsPerPage;
};
});
The correct Javscript is as follows:
JiCtrls.controller('homeCtrl', ['$scope', '$timeout', '$ionicScrollDelegate', 'DbService', 'JsonService',
function ($scope, $timeout, $ionicScrollDelegate, DbService, JsonService) {
$scope.items = [];
$scope.hasMore = true;
loadData(0);
//下拉更新
$scope.loadNewContent = function () {
loadData(2);
};
//上拉更新
$scope.loadMoreContent = function () {
loadData(1);
};
function loadData(stateType) {
if ($scope.sql == undefined) {
$scope.sql = "select top 5 * from information”;
}
DbService.getData($scope.sql, '').success(function (data, status, headers, config) {
var convertData = JsonService.convertData(data);
if (stateType == 0) {
for (var i = 0; i < convertData.length; i++) {
$scope.items.push(convertData[i]);
}
}
else if (stateType == 1) {
// $scope.items = $scope.items.concat(convertData);
for (var i = 0; i < convertData.length; i++) {
$scope.items.push(convertData[i]);
}
$timeout(function () {
$scope.$broadcast('scroll.infiniteScrollComplete');
}, 500);
}
else {
for (var i = 0; i < convertData.length; i++) {
$scope.items.unshift(convertData[i]);
}
// Stop the ion-refresher from spinning
$timeout(function () {
$scope.$broadcast("scroll.refreshComplete");
}, 500);
}
$ionicScrollDelegate.resize();
if (convertData == null || convertData.length <= 0) {
$scope.hasmore = false;
}
}).error(function (errorData, errorStatus, errorHeaders, errorConfig) {
console.log(errorData);
});
}
}
]);
I am using jqGrid 3.8.1. I want to change the pull-down values of a combobox based on the selected value of another combobox. That's why I am searching on how to change the editoptions:value of an edittype:"select".
Here's the sample jqGrid code:
<%# page pageEncoding="UTF-8" contentType="text/html;charset=UTF-8"%>
<script type="text/javascript" src="<c:url value="/js/jquery/grid.locale-ja.js" />" charset="UTF-8"></script>
<link type="text/css" rel="stylesheet" href="<c:url value="/css/jquery/ui.jqgrid.css" />"/>
<script src="<c:url value="/js/jquery/jquery.jqGrid.min.js" />" type="text/javascript"></script>
<table id="rowed5"></table>
<script type="text/javascript" charset="utf-8">
var lastsel2;
$("#rowed5").jqGrid({
datatype: "local",
height: 250,
colNames:['ID Number','Name', 'Stock', 'Ship via','Notes'],
colModel:[
{name:'id',index:'id', width:90, sorttype:"int", editable: true},
{name:'name',index:'name', width:150,editable: true,editoptions:{size:"20",maxlength:"30"}},
{name:'stock',index:'stock', width:60, editable: true,edittype:"checkbox",editoptions: {value:"Yes:No"}},
{name:'ship',index:'ship', width:90, editable: true,edittype:"select",editoptions:{value:"FE:FedEx;IN:InTime;TN:TNT;AR:ARAMEX;AR1:ARAMEX123456789"}},
{name:'note',index:'note', width:200, sortable:false,editable: true,edittype:"textarea", editoptions:{rows:"2",cols:"10"}}
],
caption: "Input Types",
resizeStop: function (newwidth, index) {
var selectedRowId = $("#rowed5").getGridParam('selrow');
if(selectedRowId) {
//resize combobox proportionate to column size
var selectElement = $('[id="' + selectedRowId + '_ship"][role="select"]');
if(selectElement.length > 0){
$(selectElement).width(newwidth);
}
}
}
,
onSelectRow: function(id){
if(id && id!==lastsel2){
//$(this).saveRow(lastsel2, true);
$(this).restoreRow(lastsel2);
$(this).editRow(id,true);
lastsel2=id;
$(this).scroll();
//resize combobox proportionate to column size
var rowSelectElements = $('[id^="' + id + '_"][role="select"]');
if(rowSelectElements.length > 0) {
$(rowSelectElements).each(function(index, element){
var name = $(element).attr('name');
var columnElement = $('#rowed5_' + name);
if(columnElement.length > 0) {
var columnWidth = $(columnElement).width();
$(element).width(columnWidth);
}
});
}
}
}
});
var mydata2 = [
{id:"12345",name:"Desktop Computer",note:"note",stock:"Yes",ship:"FedEx"},
{id:"23456",name:"Laptop",note:"Long text ",stock:"Yes",ship:"InTime"},
{id:"34567",name:"LCD Monitor",note:"note3",stock:"Yes",ship:"TNT"},
{id:"45678",name:"Speakers",note:"note",stock:"No",ship:"ARAMEX123456789"},
{id:"56789",name:"Laser Printer",note:"note2",stock:"Yes",ship:"FedEx"},
{id:"67890",name:"Play Station",note:"note3",stock:"No", ship:"FedEx"},
{id:"76543",name:"Mobile Telephone",note:"note",stock:"Yes",ship:"ARAMEX"},
{id:"87654",name:"Server",note:"note2",stock:"Yes",ship:"TNT"},
{id:"98765",name:"Matrix Printer",note:"note3",stock:"No", ship:"FedEx"}
];
for(var i=0;i < mydata2.length;i++) {
$("#rowed5").jqGrid('addRowData',mydata2[i].id,mydata2[i]);
}
</script>
Scenario:
All ship will be displayed as initial load.
If the stock column changes to Yes, ship will display only FedEx, TNT.
If the stock column changes to No, ship will display only InTime, ARAMEX, ARAMEX123456789.
How can I change the options?
I solved it by trial and error. Want to share it, please refer to the below snippet. The changes are on onSelectRow function.
onSelectRow: function(id){
if(id && id!==lastsel2){
//$(this).saveRow(lastsel2, true);
$(this).restoreRow(lastsel2);
// get the selected stock column value before the editRow
var stockValue = $("#rowed5").jqGrid('getCell', id, 'stock');
if( stockValue == 'Yes') {
$("#rowed5").jqGrid('setColProp', 'ship', { editoptions: { value: 'FE:FedEx;TN:TNT'} });
} else if( stockValue == 'No') {
$("#rowed5").jqGrid('setColProp', 'ship', { editoptions: { value: 'IN:InTime;AR:ARAMEX;AR1:ARAMEX123456789'} });
}
$(this).editRow(id,true);
lastsel2=id;
$(this).scroll();
//resize combobox proportionate to column size
var rowSelectElements = $('[id^="' + id + '_"][role="select"]');
if(rowSelectElements.length > 0) {
$(rowSelectElements).each(function(index, element){
var name = $(element).attr('name');
var columnElement = $('#rowed5_' + name);
if(columnElement.length > 0) {
var columnWidth = $(columnElement).width();
$(element).width(columnWidth);
}
});
}
}
}