I am new to knockout and sammy. I am implementing SPA using Sammy(router) and KnockOut(binding).
I have below code.
this.get('#/Page/:operation', function (context) {
this.partial('templates/Page1.html', { cache: false }).then(function (content) {
// the first argument to then is the content of the
// prev operation
$('#sammyDiv').html(content);
});
});
When I checked the console it's saying "You cannot apply bindings multiple times to the same element.(…)".
Am I doing anything wrong here?
What's the difference between Partial and Render?
Im guessing you are injecting new html into the parent which has already been bound. You should either be more specific about which divs you are binding too. I.E on apply bindings to the parent div of the html you injected, or clean and reapply bindings. I use both methods throughout my application.
if (isBound("my-div"))
ko.cleanNode(document.getElementById('my-div'));
ko.applyBindings(myModel, document.getElementById('my-div'));
Helper Function:
// Checks if Element has Already been Bound (Stops Errors Occuring if already bound as element can't be bound multiple times)
var isBound = function (id) {
if (document.getElementById(id) != null)
return !!ko.dataFor(document.getElementById(id));
else
return false;
};
I use this check before all my bindings as a safety net.
Thanks for the response and comments. Issue was with Sammy router.
this.get('#/Page/:operation', function (context) {
this.partial('templates/Page1.html', { cache: false }).then(function (content) {
// the first argument to then is the content of the
// prev operation
$('#sammyDiv').html(content);
});
});
changed to
this.get('#/Page/:operation', function (context) {
this.partial('templates/Page1.html', { cache: false });
});
It worked perfectly fine. Thanks again.
Related
I am rendering a value from django backend to frontend, and I am trying to detect the div value change with MutationObserver. Below is my current code:
MutationObserver part:
window.addEventListener('load', function () {
var element = document.getElementById('myTaskList');
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
var observer = new MutationObserver(myFunction);
observer.observe(element, {
childList: true
});
function myFunction() {
console.log("this is a trial")
console.log(element);
console.log(element.innerHTML);
}
// setTimeout(function(){
// element.innerHTML = 'Hello World!';
// }, 1000);
//
// setTimeout(function(){
// element.innerHTML = 'Hello Space!';
// }, 2000);
});
html part:
<div hidden id="myTaskList">{{resultList | safe}}</div>
I am rendering a string "dummyValue" to the div, but just don't see the value from the console.log() statements inside function.
this works well when I uncomment the setTimeout functions though.
Thanks for any help on why MutationObserver won't detect the rendered div value
I finally figured out the reason. Hope this might be helpful for people having similar issues in the future.
So, basically I was using my Django form submit button to do two actions at one time:
1. submit data to the view and process the data in the view;
2. trigger another function with the click action through
Ajax.
The second action was blocked by the first action, and I was only able to get result from action 1.
My solution: I modified action 1 to use Ajax as well. As I mentioned above, I originally used the Django form to submit data. I trigger action 2 inside the success function of action 1. Everything is working well now.
I use controllerAs and avoid using the $scope as a model.
I want to add pagination in my search result. I have a paging object in my controller as below, which will be shared with angular service and then with the server API.
vm.paging = {
pageNumber: 1,
pageSize: 10,
totalCount: 0
};
totalCount will be set by server and then be read from response.
My pagination in html:
<pagination total-items="ctrl.pageCount"
items-per-page="ctrl.paging.pageSize"
ng-model="ctrl.paging.pageNumber"
class="pagination-sm"
boundary-links="true"
ng-change="ctrl.find()">
</pagination>
I calculate pageCount in controller as below:
vm.pageCount = function () {
return Math.ceil(vm.paging.totalCount / vm.paging.pageSize);
};
But it doesn't work. when I set vm.pageCount = 1000; it works. It seems the problem is the function. How can I fix this problem? (the buttons are disabled and It should return 100 pages, not one! )
Update: Sample Plunker
you CAN use an Immediately Invoked Function Expression (IFEE) to get your vm.pageCount like so
ctrl.pageCount = (function() {
return Math.ceil(ctrl.paging.totalItems / ctrl.paging.pageSize);
})();
but you don't need to calculate pageCount and put it under total-items (the amount of pages in the pagination will be calculated automatically), you should provide it the length of you record array instead!
lets say if you get table data vm.data from http, you should place the length of this data inside total-items like so
total-items="vm.data.length"
HTML
<uib-pagination total-items="ctrl.paging.totalItems"
items-per-page="ctrl.paging.pageSize"
ng-model="ctrl.paging.pageNumber"
class="pagination-sm"
boundary-links="true"
ng-change="ctrl.find()">
</uib-pagination>
JS
ctrl.paging = {
pageNumber: 1,
pageSize: 10,
totalItems: response.length // this is your remote data
};
I've made this plunker for you
EDIT 1
since version 0.14.0, the directives are prefixed by uib-
Since version 0.14.0 we started to prefix all our components. If you are upgrading from ui-bootstrap 0.13.4 or earlier, check our migration guide.
so you should also use uib-pagination (instead of pagination) because your plunk uses the 1.3.2 version.
I did it! Of course, with the guidance of my colleague :) . As in my plunk (script.js), I hadn't Initialized the vm.paging object after the response, so the computing was based on default values in vm.paging. Since totalCount was 0, always the Division result was 1 and I had just one page. So I edited the vm.find body as below:
vm.find = function () {
vm.result.length = 0;
vm.findingPromise = myService.find(vm.filter, vm.paging);
vm.findingPromise
.then(function (response) {
vm.result = response.data.result;
// EDITED PART:
vm.paging = response.data.paging;
//
}, function (response) {
var r = response;
ngNotify.set('Some problems occurred!',
{
type: 'error',
position: 'top',
sticky: true
});
});
And also, I couldn't take advantages of uib-pagination, but when I replaced ui-bootstrap-tpls-1.3.2.min.js anywhere I was using ui-bootstrap-tpls.min.js before, it works correctly.
Also I removed the function of computing pageCount. With many thanks to svarog.
I was wondering if it was possible to pass a function foo() as an attribute func="foo()" and have it called this.func() inside of the polymer element?
<foo-bar func="foo()"></foo-bar>
Polymer({
is: 'foo-bar',
properties: {
func: Object,
},
ready: function() {
this.func();
}
});
I've been trying to get this working for ages with no luck.
Thanks in advance.
<foo-bar func="foo()"></foo-bar>
Polymer({
is: 'foo-bar',
properties: {
func: {
type: String, // the function call is passed in as a string
notify: true
},
attached: function() {
if (this.func) {
this.callFunc = new Function('return '+ this.func);
this.callFunc(); // can now be called here or elsewhere in the Polymer object
}
});
So the trick is that "foo( )" is a string when you first pass it to the Polymer element. I fought with this for a while as well and this is the only way I could find to get it done. This solution creates a function that returns your function call, which you assign as the value of one of your polymer element properties.
Some people might say you shouldn't use the Function constructor because it is similar to eval( ) and.... well you know, the whole 'eval is evil' thing. But if you're just using it to return a call to another function and you understand the scope implications then I think this could be an appropriate use-case. If I'm wrong I'm sure someone will let us know!
Here's a link to a nice SO answer about the differences between eval( ) and the Function constructor in case it can help: https://stackoverflow.com/a/4599946/2629361
Lastly, I put this in the 'attached' lifecycle event to be on the safe side because it occurs later than 'ready'. I'm not sure if an earlier lifecycle event or 'ready' could be used instead of 'attached'. Perhaps someone can improve this answer and let us know.
This is my first try to make a single page application with HTML5. I'm using jquery, knockout and sammy.
Code: http://codepaste.net/apdrme
The problem is that I don't know what I'm doing wrong. I know it is the following:
this.get("#/", function() {
this.personList(this.persons);
});
But how else can I populate the list?
You could populate your list as follows:
function ViewModel() {
this.personList = ko.observableArray([{"name":"Josh"}, {"name":"Barry"}, {"name":"Mike"}]);
};
[...]
ko.applyBindings(new ViewModel());
Pay attention to use ko.observableArray() at the declaration. So, you could also remove the argument and call this.personList([{"name":"Josh"}, {"name":"Barry"}, {"name":"Mike"}]) in your main Sammy route and fill the list with other values in another route.
Another mistake is that you have used the with-binding that is not necessary here. Check the documentation about it.
You would normally use jQuery and an ajax call to populate personList. personList should be an ko.observableArray.
this.personList = ko.observableArray();
this.get("#/", function() {
$.ajax({url:"/api/persons/", dataType: 'json', success:function(persons){
this.personList(persons);
}});
});
I created a directive with a method that should be called from other elements that are not part of the directive. However it looks like this method is not exposed.
Some example jade code to clarify:
//- a controller for the view itself
div(ng-controller="someController")
//- this is part of the view itself, not within the directive
div(ng-repeat="element in elements")
div(ng-click="methodFromDirective(element)") click element {{$index}} to trigger directive
//- this is the directive
div(some-directive)
The someController isn't too important here I think. It has methods but NOT the methodFromDirective(element) one. The methodFromDirective(element) is a method that exists only in the directive.
If I make a directive and put some logging on creation I can clearly see it's created. However the methodFromDirective(element) method isn't exposed so the calls aren't properly triggered.
The methodFromDirective(element) itself will only work on elements from within the directive's template.
some coffeescript to show the definition of the the directive (ignore indentation errors here):
'use strict'
define [], () ->
someDirective = () ->
restrict: 'A'
scope: {
show: '='
}
transclude: false
templateUrl: 'someTemplateHere.html'
controller = ($scope) ->
# exposing the method here
$scope.methodFromDirective(element)->
$scope.theMethod element
link = (scope, element, attr) ->
# this is logged
console.log "init someDirective"
# triggering this method form outside fails
scope.theMethod = (element)->
console.log "method triggered with element", JSON.stringify(element)
I found my issue.
From the angularJS documentation on directives I was looking into the transclude option since that states:
What does this transclude option do, exactly? transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.
I combined transclude=false with the controller function since that exposes the method, again from docs:
Savvy readers may be wondering what the difference is between link and controller. The basic difference is that controller can expose an API, and link functions can interact with controllers using require.
However what I missed completely was that I isolated scope within my directive. From docs:
What we want to be able to do is separate the scope inside a directive from the scope outside, and then map the outer scope to a directive's inner scope. We can do this by creating what we call an isolate scope. To do this, we can use a directive's scope option:
So even if you use transclude=false and the controller function you'll still fail to expose methods if you use isolated scope! Lesson learned!
While figuring out what went wrong I also made a fiddle for better understanding: http://jsfiddle.net/qyBEr/1/
html
<div ng-app="directiveScopeExample">
<div ng-controller="Ctrl1">
<p>see if we can trigger a method form the controller that exists in the directive.</p>
<ul>
<li>Method in Controller</li>
<li>Method in Directive</li>
</ul>
<simple-directive/>
</div>
</div>
javascript
angular.module('directiveScopeExample', [])
.controller('Ctrl1', function Ctrl1($scope) {
$scope.methodInController = function(){
alert('Method in controller triggered');
};
})
.directive('simpleDirective', function(){
return {
restrict: 'E',
transclude: false,
controller: function($scope){
$scope.methodInDirective = function(){
// call a method that is defined on scope but only within the directive, this is exposed beause defined within the link function on the $scope
$scope.showMessage('Method in directive triggered');
}
}
// this is the issue, creating a new scope prevents the controller to call the methods from the directive
//, scope: {
// title: '#'
//}
, link: function(scope, element, attrs, tabsCtrl) {
// view related code here
scope.showMessage = function(message){
alert(message);
}
},
//templateUrl: 'some-template-here.html'
};
})
Calling private methods inside directive's link function is very simple
dropOffScope = $('#drop_off_date').scope();
dropOffScope.setMinDate('11/10/2014');
where
$('#drop_off_date') - jQuery function
setMinDate() - private function inside directive
You can call directive function even from outer space.
By default the scope on directive is false meaning directive will use the parent's scope instead of creating a new one. And hence any function or model defined in the directive will be accessible in the parent scope. Check out this.
I think your problem can be solved as follows:
angular.module('directiveScopeExample', [])
.controller('Ctrl1', function Ctrl1($scope) {
$scope.methodInController = function(){
alert('Method in controller triggered');
};
})
.directive('simpleDirective', function(){
return {
restrict: 'E',
scope: false,
link: function(scope, element, attrs, tabsCtrl) {
// view related code here
scope.showMessage = function(message){
alert(message);
}
},
//templateUrl: 'some-template-here.html'
};
This approach might be an issue in case you want to create reusable directives and you are maintaining some state/models in your directive scope. But since you are just creating functions without side-effects, you should be fine.