I have parent controller and a child controller setup so that the child controller can toggle a property from the parent controller.
When I toggle the property from the Child Controller, it successfully updates the child view and triggers the "onlineChanged" observer and logs the appropriate value, but the parent view doesn't update with the correct value. The display remains false.
Here are is my code:
App.Router.map(function() {
this.resource('Main', function() {});
});
App.MainController = Ember.Controller.extend({
online: false
onlineChanged: function () {
console.log(this.get('online'));
}.observes('online')
});
App.MainIndexController = Ember.Controller.extend({
needs: ['Main'],
actions: {
toggleOnline: function() {
this.toggleProperty('controllers.Main.online');
}
}
});
And my templates;
Main.hbs
{{outlet}}
Online: {{online}}
Index.hbs
<button {{action toggleOnline}}>toggle</button>
Why is it that the console logs the appropriate value, but Main.hbs doesn't update correctly?
Main should be lowercase in your toggle property, needs, and map. I'm not positive why that would break it though.
Additionally a comma is missing in your main code, so I assume you've ripped out some other logic that could be breaking it.
http://emberjs.jsbin.com/EKepEMaD/1/edit
Related
I have this code https://jsfiddle.net/delux123/62Lmskbx/11/ where user can draw circle, segment and arrow-segment. When each annotation is selected, there is a "delete" button available, which should normally delete the selected annotation.
But this does not works! I tried the following ways for removing them:
1 ) using thischart.currentAnnotation.destroy() will delete the annotation (works for all three types) but then all the further actions stops from working
document
.querySelectorAll('.highcharts-popup-annotations .deletebutton')[0]
.addEventListener(
'click',
function() {
thischart.currentAnnotation.destroy(); //this stops further drawing
thischart.annotationsPopupContainer.style.display = 'none';
}
);
2 ) using deletion by ID thischart.removeAnnotation(thischart.currentAnnotation.options.id) does not works, since the annotations are created dynamically and they do not have IDs assigned to them.
document
.querySelectorAll('.highcharts-popup-annotations .deletebutton')[0]
.addEventListener(
'click',
thischart.removeAnnotation(thischart.currentAnnotation.options.id); //this requires elements to have IDs (which they do not have)
thischart.annotationsPopupContainer.style.display = 'none';
}
);
For the second approach, I even tried to intersect the drawing and to assign a random string as an ID (since the reason that deletion by id does not works, is because the ID is undefined). So under navigation -> bindings I added the object:
circleAnnotation: {
start: function(e) {
var navigation = this.chart.options.navigation;
return this.chart.addAnnotation(
Highcharts.merge({
id: randomStr() //this is a method that generates random string
},
navigation
.annotationsOptions,
navigation
.bindings
.circleAnnotation
.annotationsOptions
)
);
}
}
The both approaches are not working.
You can pass a direct annotation object to the removeAnnotation method:
thischart.removeAnnotation(thischart.currentAnnotation);
Live demo: https://jsfiddle.net/BlackLabel/5ycf3s4o/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Chart#removeAnnotation
I am trying to get a piece of code to work where I don't have total control over half of the code. In short, there is 1 main controller that has an object that is then obtained from a second controller. When main controller 1 updates the object, controller 2 never sees it. I think this is because the 2 controllers aren't watching the object/properties (copies?). If you notice, the Angular Binding to {{Title}}, this is where the issue is visible as the "Title" never gets updated in the second controller.
Here is some sample code that shows the problem. Currently, the code does a 3 seconds loop to get the object again, and reassign it to the second controller.
The code here I can't really touch. I have source, but it spaghetti and I am just generalizing what is here.
// html I can't really change, outside my world.
<div id="mainApp" ng-app="MainApp" ng-controller="mainController">
</div>
// code I can't "really" change, non-angular (can't use $http).
$ajax(get...)
.success(function (result) {
$('#element').html(result);
})
The code below is fairly separated and I can tinker with it. The HTML is returned from a service called by the $ajax call above.
// code I can change (the "result", or html returned from the service)
// containerController.js
var containerController = function ($scope, $timeout) {
$scope.models = {
item: null;
}
$scope.getItem = function() {
var mainAppScope = angular.element($('#mainApp')).scope();
$scope.models.item = mainAppScope.GetItem();
}
$scope.getItem();
// HACK WORK AROUND
// Get the item from the mainController.
var itemSync = setInterval(function () {
$scope.getItem();
$scope.$apply();
}, 3000);
}
The HTML returned from the service (it's really an ASP.NET MVC Partial View)
// HTML
<div id="container" ng-app="containerApp" ng-controller="containerController">
<!-- This will bind the first time, but won't syncronize when other controller updates -->
<!-- the controller is currently doing a loop to do so, not good. -->
<div>{{models.item.Title}}</div>
</div>
<script type="text/javascript" src="~/Scripts/controllers/containerController.js"></script>
<script type="text/javascript">
// This will inject the controller and new app into angular.
var container = document.getElementById('container');
var containerApp = angular.module('containerApp', []);
containerApp.controller('containerController', ['$scope', containerController]);
angular.bootstrap(angular.element(container), ['containerApp']);
</script>
You could change hacky part to use $interval which will manage to run digest cycle after each interval
var itemSync = $interval(function () {
$scope.getItem();
}, 3000);
To sync object from parent controller to child controller you could use object structure of model, that will is nothing but Javascript prototypal & will update data.
$scope.pageData = {}; //declare this in parent controller
$scope.pageData.title = 'Title 1' //use this where you want to change in child controller
Given the dart code
class LandingController {
bool hideDiv = false;
void doit() {
new JsObject(context['loginControls']).callMethod('fadeLogin', [() {
print(hideDiv);
hideDiv = true;
print(hideDiv);
print("WTF");
}]);
}
}
which calls the JS:
var loginControls = function() {
this.fadeLogin = function(func) {
$('#landingHeader').animate({ opacity: 0 }, 500, func);
}
};
which should affect the view:
<button ng-click="doit();">Click</button>
<div id="landingHeader">Hide me after button click</div>
<div ng-if="ctrl.hideDiv == false"><img src="assets/img/ajax-loader-small.gif">Waiting for div to disappear...</div>
After a button click and a 500 millisecond delay I see in my console a "WTF" print correctly. The div, however, is still visible. Once a user action occurs, in my case a mouse click, the div magically disappears. It seems as though the controller's value is changed, but the browser doesn't receive the change to hide the div, as I've printed the controller's value in the anonymous callback.
There is a work around, but it involves setting Dart timer's to the same fade times that you use in the javascript after the JsObject call and setting your controller's values in those Timer callbacks - gross but it works.
I think you need to call scope.apply(). I think Angular just can't recognize the value change in hideDiv when doit() is called from another zone (like JS).
You usually don't need to call scope.apply() in Angular.dart but I think this is one of the exceptions.
Is the animate function the only reason you use jQuery? It might be easier to do this with Angular.darts animation features.
Im trying to use pass my car value to another function, which i have no idea how. i tried to place the whole function btn-info-add into .span8. But this it will execute twice on the 2nd time.
$(".span8").on("click", "table #trID", function() {
var car = ($(this).closest("tr").children("td").eq(1).html());
$('#myModal1').modal('show');
});
$("#btn-info-add").click(function() //button inside the modal
selectCourse(car); //execute ajax
});
var car; //car must be declared out of your function to be available for both functions
$(".span8").on("click", "table #trID", function() {
car = ($(this).closest("tr").children("td").eq(1).html());
$('#myModal1').modal('show');
});
$("#btn-info-add").click(function() //button inside the modal
selectCourse(car); //execute ajax
});
You can create a hidden element inside your dialog (input would be great) and assign it your desire value.
<div id="dialog" title="Basic dialog">
<p>This is the default dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the 'x' icon.</p>
<input id="carvalue" type="hidden" value=""/>
</div>
Note that I created an input element (hidden, of course) which is going to store the value that I want to access later. After that, you can modify your code like this:
$(".span8").on("click", "table #trID", function() {
var car = ($(this).closest("tr").children("td").eq(1).html());
$("#carvalue").val(car); // Store value into hidden input
$('#myModal1').modal('show');
});
$("#btn-info-add").click(function() //button inside the modal
var car = $("#carvalue").val(); // retrieve value from input hidden
if(car != ""){
selectCourse(car);
}
});
This technique is commonly used in forms to pass additional information on AJAX calls. Your user will not notice its presence and you can keep working. Happy coding!
EDIT:
JQuery has a method called jQuery.data to store information into JQuery elements. So your values are going to be stored on the element itself. Your code will look like this:
$(".span8").on("click", "table #trID", function() {
var car = ($(this).closest("tr").children("td").eq(1).html());
jQuery.data("#btn-info-add", "car", car); // store data inside jQuery element
$('#myModal1').modal('show');
});
$("#btn-info-add").click(function() //button inside the modal
selectCourse(jQuery.data("#btn-info-add", "car")); //execute ajax
});
I hope it helps you. Happy coding!
I'm stuck on one particular part of my project which consists of the components mentioned in the title.
I currently have a proof of concept that works the way I want it to work:
Sammy is integrated into the knockout viewmodels (as per the tutorial
on the knockout site)
the views are loaded on demand by a controller
(so I don't have to define every single view on the application page)
In my current situation I instance the viewmodels when the application starts (if I don't instance them, Sammy will not handle the routing). The problem is where the view is loaded and swapped by Sammy. I have to make a call to ko.applyBindings for KO to bind to the view. But its bad practice to repeatedly call applybingings.
My question, how do I bind to my views that are loaded on demand? I can't call ko.applybindings since that would create a memoryleak when the view is loaded more than once.
Here is an example VM with the offending ko.applyBindings:
function serviceInfoVm() {
var self = this;
self.ObjectKey = ko.observable();
self.Service = ko.observable();
self.LoadService = function () {
$.get('ServiceData/Detail', { serviceId: self.ObjectKey() }, function (data) {
self.Service(data);
});
};
$.sammy('#content', function () {
this.get('#/service/:id', function (context) {
var ctx = context;
self.ObjectKey(this.params['id']);
self.LoadService();
$.get('Content/ServiceInfo', function (view) {
ctx.app.swap(view);
ko.applyBindings(self);
});
});
}).run();
};
Anyone with some pointers and/or solutions to this problem?
You have the Sammy code in the viewmodel, which can work great if that viewmodel will be present and you want sub viewmodels and views to be loaded. So I assume that is what you are trying to do. Food for thought ... separate the sammy code into its own module (I call mine router in router.js) and let it manage the navigation separate from any viewmodel.
But back to your code ... you could set up your subviews and subviewmodels and use applybindings on them prior to the sammy.get being called. Basically, you are registering your routes in advance. Then the sammy.get just navigates to the new view, which is already data bound.
Not a solution but another approach:
Ended up abandoning the idea of loading the views dynamically.
Now my views are always present in the page and the visibility is triggered by this code:
var app = function () {
var self = this;
self.State = ko.observable('home');
self.Home = ko.observable(new homepageVm());
self.User = ko.observable(new userInfoVm());
$.sammy(function () {
this.get('#/', function (context) {
self.State('home');
});
this.get('#/info/:username', function (context) {
self.State('user');
self.User().UserName(context.params['username']);
self.User().LoadInfo();
});
}).run();
};
And the div visibility is triggered this way:
<div id="homeView" data-bind="with: Home, visible: State() === 'home'">
This way the ko.applyBindings only needs to be called once when the app starts.
The viewmodel above is bound to our shell page.
More on this here
Calling applyBindings on the specific element in the returned template is an option:
ko.applyBindings(viewModel, htmlNode)
Also see this question with regard to lazy loading templates: knockout.js - lazy loading of templates
And docs here for applyBindings: http://knockoutjs.com/documentation/observables.html