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');
});
};
})
Related
Creating a map with markers displayed on it. When clicking a marker, this one has to display a Popup. I extended the L.Popup like this
L.InfrastructurePopup = L.Popup.extend({
options: {
template : "<form id='popup-form'>\
<div>\
<label for='problem'>Problem</label>\
<textarea id='problem' rows='4' cols='46' placeholder='Type your text here'></textarea>\
</div>\
<div>\
<label for='solution'>Solution</label>\
<textarea id='solution' rows='4' cols='46' placeholder='Type your text here'></textarea>\
</div>\
<button id='button-submit' class='btn btn-primary' type='button'>Submit</button>\
</form>",
},
setContent: function () {
this._content = this.options.template;
this.update();
return this;
},
initializeForm(layer, callback)
{
var problem = L.DomUtil.get('problem');
problem.textContent = layer.options.problem ? layer.options.problem : "";
problem.addEventListener('change', (e) =>
{
layer.options.problem = problem.value;
});
var solution = L.DomUtil.get('solution');
solution.textContent = layer.options.solution ? layer.options.solution : "";
solution.addEventListener('change', (e) =>
{
layer.options.solution = solution.value;
});
var buttonSubmit = L.DomUtil.get('button-submit');
buttonSubmit.addEventListener('click', (e) =>
{
callback(layer);
});
}
});
L.infrastructurePopup = function (options, source)
{
return new L.InfrastructurePopup(options, source);
};
I linked it into a custom Marker called InfrastructureMarker that has one and only popup , a InfrastructurePopup. So when it calls the openPopup() function it loads the popup on the map [ map.addLayer(popup) ] and give me the correct datas thanks to method initializeForm() that I call after the addLayer(popup) method.
L.Map.include({
openInfrastructurePopup: function (layer, callback)
{
this.closePopup();
layer._popup._isOpen = true;
this.addLayer(layer._popup);
layer._popup.initializeForm(layer, callback);
}
});
L.InfrastructureMarker = L.Marker.extend({
openPopup: function (callback)
{
if (this._popup && this._map && !this._map.hasLayer(this._popup))
{
this._popup.setLatLng(this._latlng);
this._map.openInfrastructurePopup(this, callback);
}
return this;
},
togglePopup: function (callback)
{
if (this._popup)
{
if (this._popup._isOpen)
{
this._popup._isOpen = false;
this.closePopup();
}
else
{
this.openPopup(callback);
}
}
return this;
},
bindPopup: function (callback, options)
{
var anchor = L.point(this.options.icon.options.popupAnchor || [0, 0]);
anchor = anchor.add(L.Popup.prototype.options.offset);
if (options && options.offset)
{
anchor = anchor.add(options.offset);
}
options = L.extend({offset: anchor}, options);
if (!this._popupHandlersAdded)
{
this
.on('click', () => {this.togglePopup(callback)}, this)
.on('remove', this.closePopup, this)
.on('move', this._movePopup, this);
this._popupHandlersAdded = true;
}
this._popup = new L.infrastructurePopup(options, this).setContent();
return this;
},
});
L.infrastructureMarker = function (latlng, options)
{
return new L.InfrastructureMarker(latlng, options);
};
But if I decide to click on one marker, then on another one without closing the first one, it loads the template, but initializeForm(callback) doesn't change the datas. I checked all the datas to know if it was empty or something but everything worked, I absolutely don't know where the problem is. I suppose the popup is not yet set on the DOM before my L.DomUtils.get fire but I shouldn't see undefined elements in console.log when I'm getting them.
I actually found what was happening :
Actually, when the L.map calls its closePopup function , it destroys the layer.
So after that, it creates a new one to display. BUT the remaining HTML from the previous kind of still exists.
So I finally bound exact same Ids to two HTML tags. Heresy !
My solution became what's next :
L.InfrastructurePopup = L.Popup.extend({
setContent: function (layer)
{
var template = "<form id='popup-form'>\
<div>\
<label for='problem'>Problème Identifié</label>\
<textarea id='" + layer._leaflet_id + "-problem' rows='4' cols='46' placeholder='Type your text here'></textarea>\
</div>\
<div>\
<label for='solution'>Solution Proposée</label>\
<textarea id='" + layer._leaflet_id + "-solution' rows='4' cols='46' placeholder='Type your text here'></textarea>\
</div>\
<button id='" + layer._leaflet_id + "-button-submit' class='btn btn-primary' type='button'>Submit</button>\
</form>";
this._content = template;
this.update();
return this;
},
initializeForm: function(layer, callback)
{
console.log(L.DomUtil.get(layer._leaflet_id+'-problem'));
var problem = L.DomUtil.get(layer._leaflet_id + '-problem');
problem.textContent = layer.options.problem ? layer.options.problem : "";
problem.addEventListener('change', (e) =>
{
layer.options.problem = problem.value;
});
var solution = L.DomUtil.get(layer._leaflet_id + '-solution');
solution.textContent = layer.options.solution ? layer.options.solution : "";
solution.addEventListener('change', (e) =>
{
layer.options.solution = solution.value;
});
var buttonSubmit = L.DomUtil.get(layer._leaflet_id + '-button-submit');
buttonSubmit.addEventListener('click', (e) =>
{
callback(layer);
});
}
});
L.infrastructurePopup = function (options, source)
{
return new L.InfrastructurePopup(options, source);
};
Calling setContent when creating my InfrastructurePopup with the layer_id and set it into my template made it work.
I got : '97-problem' or '99-problem' and '97-solution' or '99-solution
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')
);
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);
});
}
}
]);
Today our user reported that saving his CV does not work, that it does not save his Skills, languages, driving license level, schools and prev. employments.
That is the Collection forms that i use on 2 parts of website (CV and Offers)...
Funny is that we tested it before going live and running live from IE6 to any other newer browser.
Collections is added correctly using "add foobar record" button, when there is any record in DB it appears in edit correctly, when i edit these existing it will be saved, if i remove them than they will be removed.
But when i add new, these new records is not in Form post data. I cant understand if its part of form, html is rendered correctly, why it does not include it in post...
These collections works with Offer entity, saved updated added... no problem. i have checked the controller code, the javascript code, the entity code, the html code, the collection templates, the form types..
Here is DB structure:
here is how i add collection in botz CV and Offer
<div class="tbl">
<div class="row">
<div class="col" style="text-align: center; width: 100%;">Počítačové znalosti</div>
</div>
<div class="divider"></div>
<div class="skills" data="0" data-prototype="{% filter escape %}{% include 'TvarplastTopzamBundle:Collections:SkillCollection.html.twig' with {'form': form.skills.vars.prototype} %}{% endfilter %}">
{% for skill in form.skills %}
<div class="row">
{% include 'TvarplastTopzamBundle:Collections:SkillCollection.html.twig' with {'form': skill} %}
</div>
<script type="text/javascript">$(".skills").data("index", {{ loop.index }});</script>
{% endfor %}
</div>
<div class="row">
<div class="col">
Pridať počítačovú znalosť
</div>
</div>
</div>
Problem cannot be with entity, because if some relation exists in DB that is displayed as collection, and if its edited, it can be changed or removed, and its displayed in post parameters, then entity, form type, cannot be wrong.
but i handle form like this:
public function zivotopisAction(\Symfony\Component\HttpFoundation\Request $request, $showmsg = false) {
if (!$this->get("data")->hasPerm(Role::WORKER, $this->getUser())) {
$message["show"] = true;
$message["text"] = "Nemáte požadované oprávnenia. Stránka nemôže byť zobrazená.";
$message["type"] = "box-red";
return new \Symfony\Component\HttpFoundation\Response($this->renderView("TvarplastTopzamBundle::error.html.twig", array("message" => $message)));
}
$return = array();
$message = array("show" => $showmsg, "type" => "", "text" => "");
if ($message["show"]) {
$message["text"] = "Je nutné vyplniť nasledujúce informácie pre pokračovanie.";
$message["type"] = "box-red";
}
$em = $this->getDoctrine()->getManager();
if (!is_null($this->getUser()->getZivotopis())) {
$zivotopis = $em->getRepository("TvarplastTopzamBundle:Zivotopis")->find($this->getUser()->getZivotopis()->getId());
} else {
$zivotopis = new \Tvarplast\TopzamBundle\Entity\Zivotopis();
}
$originalSkills = new \Doctrine\Common\Collections\ArrayCollection();
if ($zivotopis->getSkills()) {
foreach ($zivotopis->getSkills() as $skill) {
$originalSkills->add($skill);
}
}
$originalLanguages = new \Doctrine\Common\Collections\ArrayCollection();
if ($zivotopis->getLanguages()) {
foreach ($zivotopis->getLanguages() as $language) {
$originalLanguages->add($language);
}
}
$originalDrivingskills = new \Doctrine\Common\Collections\ArrayCollection();
if ($zivotopis->getSkilldriving()) {
foreach ($zivotopis->getSkilldriving() as $skilldriving) {
$originalDrivingskills->add($skilldriving);
}
}
$originalEmployments = new \Doctrine\Common\Collections\ArrayCollection();
if ($zivotopis->getEmployments()) {
foreach ($zivotopis->getEmployments() as $employment) {
$originalEmployments->add($employment);
}
}
$originalSchools = new \Doctrine\Common\Collections\ArrayCollection();
if ($zivotopis->getSchools()) {
foreach ($zivotopis->getSchools() as $school) {
$originalSchools->add($school);
}
}
$form = $this->createForm(new \Tvarplast\TopzamBundle\Form\ZivotopisType(), $zivotopis, array(
'action' => $this->generateUrl('zivotopis'),
));
$form->handleRequest($request);
if ($form->isValid()) {
//var_dump($_POST); die();
foreach ($originalSkills as $skill) {
if (false === $zivotopis->getSkills()->contains($skill)) {
$skill->getZivotopis()->removeElement($zivotopis);
$em->persist($skill);
$em->remove($skill);
}
}
foreach ($originalLanguages as $language) {
if (false === $zivotopis->getLanguages()->contains($language)) {
$language->getZivotopis()->removeElement($zivotopis);
$em->persist($language);
$em->remove($language);
}
}
foreach ($originalDrivingskills as $drivingskill) {
if (false === $zivotopis->getSchools()->contains($drivingskill)) {
$drivingskill->getZivotopis()->removeElement($zivotopis);
$em->persist($drivingskill);
$em->remove($drivingskill);
}
}
foreach ($originalEmployments as $employment) {
if (false === $zivotopis->getEmployments()->contains($employment)) {
$employment->getZivotopis()->removeElement($zivotopis);
$em->persist($employment);
$em->remove($employment);
}
}
foreach ($originalSchools as $school) {
if (false === $zivotopis->getSchools()->contains($school)) {
$school->getZivotopis()->removeElement($zivotopis);
$em->persist($school);
$em->remove($school);
}
}
$zivotopis->upload();
$zivotopis->setBasicUser($this->getUser());
$zivotopis = $form->getData();
$em->persist($zivotopis);
$em->flush();
$message["text"] = ($this->container->get('security.context')->isGranted('ROLE_WORKER') ? "Životopis" : "Profil") . " bol úspešne uložený.";
$message["type"] = "box-yellow";
$message["show"] = true;
}
$return["form"] = $form->createView();
$return["message"] = $message;
return $return;
and my javascript looks like this:
$(document).ready(function() {
$.extend({getDeleteLinkCode: function(div) {
return '<div class="col" style="margin-top: 8px; margin-left: 3px;"><a href="#" style="margin-top: 5px;" >Odstrániť</a></div>';
}});
$.extend({addSubFormSelectChangeListener: function(collectionHolder, formRow, div) {
formRow.find('select' + (!div ? ':first' : '')).on("change", function() {
var org = $(this);
if (collectionHolder.find(!div ? "tr" : "div").size() > 1) {
collectionHolder.find(!div ? "tr" : "div").each(function() {
if (org.val() === $(this).find('select:first').val() && org.attr("id") !== $(this).find("select:first").attr("id")) {
org.parent().parent().remove();
}
});
}
});
}});
$.extend({addSubForm: function(collectionHolder, div) {
var prototype = collectionHolder.data('prototype');
var index = collectionHolder.data('index');
index = (index !== parseInt(index) ? 0 : index);
var form = prototype.replace(/__name__/g, index);
var formRow = $((div ? '<div class="row"></div>' : '<tr></tr>')).append(form);
var removeFormRow = $($.getDeleteLinkCode(div));
formRow.append(removeFormRow);
collectionHolder.data('index', index + 1);
collectionHolder.append(formRow);
removeFormRow.on('click', function(e) {
e.preventDefault();
formRow.remove();
});
$.addSubFormSelectChangeListener(collectionHolder, formRow, div);
}});
function addSubFormItemDeleteLink(collectionHolder, $tagFormLi, div, notag) {
var $removeFormA = $($.getDeleteLinkCode(div));
$tagFormLi.append($removeFormA);
$removeFormA.on('click', function(e) {
e.preventDefault();
$tagFormLi.remove();
});
$.addSubFormSelectChangeListener(collectionHolder, $tagFormLi, div);
}
jQuery.fn.toggleOption = function(show) {
$(this).toggle(show);
if (show) {
if ($(this).parent('span.toggleOption').length) {
$(this).unwrap();
}
} else {
if ($(this).parent('span.toggleOption').length === 0) {
$(this).wrap('<span class="toggleOption" style="display: none;" />');
}
}
};
$.extend({comboFilter: function(inputField, comboBox) {
$("#" + inputField).delayBind("input", function() {
var inputValue = $(this).val().toLowerCase();
var combobox = document.getElementById(comboBox);
$("#" + comboBox).children("span").children("optgroup").each(function() {
$(this).toggleOption(true);
});
optionToSelect = false;
$("#" + comboBox + " option").each(function() {
if ($(this).text().toLowerCase().replace(/<.+?>/g, "").replace(/\s+/g, " ").indexOf(inputValue.replace(/<.+?>/g, "").replace(/\s+/g, " ")) !== -1) {
optionToSelect = $(this);
$(this).toggleOption(true);
} else {
$(this).toggleOption(false);
}
if (optionToSelect !== false) {
$(optionToSelect).select();
}
});
$("#" + comboBox).children("optgroup").each(function() {
if ($(this).children("option").length <= 0) {
$(this).toggleOption(false);
} else {
$(this).toggleOption(true);
}
});
if ($("#" + comboBox).children("optgroup").length <= 0) {
$("#" + comboBox).children("span").children("optgroup").children("option").each(function() {
$(this).parent().toggleOption(true);
});
}
if (inputValue === '') {
combobox[0].selected = true;
$("#" + comboBox).children("span").children("optgroup").each(function() {
$(this).toggleOption(true);
});
}
}, 50);
}});
/* skills */
holderSkills = $('div.skills');
holderSkills.find('div.row').each(function() {
addSubFormItemDeleteLink(holderSkills, $(this), false, false);
});
$(".add_skill_link").on('click', function(e) {
e.preventDefault();
$.addSubForm(holderSkills, true);
});
/* driving */
holderDriving = $('div.skilldriving');
holderDriving.find('div.deletehere').each(function() {
addSubFormItemDeleteLink(holderDriving, $(this), true, true);
});
$(".add_driving_link").on('click', function(e) {
e.preventDefault();
$.addSubForm(holderDriving, true);
});
/* Lang */
holderLanguages = $('div.languages');
holderLanguages.find('div.row').each(function() {
addSubFormItemDeleteLink(holderLanguages, $(this), false, false);
});
$(".add_lang_link").on('click', function(e) {
e.preventDefault();
$.addSubForm(holderLanguages, true);
});
/* Emp */
holderEmployments = $('div.employments');
holderEmployments.find('div.deletehere').each(function() {
addSubFormItemDeleteLink(holderEmployments, $(this), false, false);
});
$(".add_zam_link").on('click', function(e) {
e.preventDefault();
$.addSubForm(holderEmployments);
});
/* Schools */
holderSchools = $('div.schools');
holderSchools.find('div.deletehere').each(function() {
addSubFormItemDeleteLink(holderSchools, $(this), false, false);
});
$(".add_Schools_link").on('click', function(e) {
e.preventDefault();
$.addSubForm(holderSchools);
});
});
Any idea where can the problem be?
Thank you very much
Thanks to Dynamically added form field not showing up in POSTed data
Solved by switching first 2 lines to be {form} first and div the second
i had something like
<div .....>
{form....}
some html
</div>
<div>
htmlhhtml
</div>
</div>
{endform}
That was the reason why browser was not able to read newly added items