I wondered if it's possible to append a partial to the view, instead of replacing the previous one.
The usecase is as follows:
I'm building a questionaire
Upon starting the questionaire only 1 question is visible/in the DOM
The answer of question 1 dictates what question 2 should be
Upon answering question 1, question 2 gets appended to the DOM, under question 1
If question 1 is changed, all other questions are reset/removed from dom, and a fresh, unanswered question 2 appears (under 1)
Maybe using one partial a question is not the way to go, in that case please let me know what the preferred method would be (vanilla/no-angular JS?)
To give you a rough idea:
Given your app is in angularjs, you should be having all your questions inside a model in a controller:
$scope.questions = [
{
"question":"foo?",
"options":["bar1","bar2","bar3","bar4"]
"answer":2
},
{
},
{
}
];
// initially show the 1st question
$scope.currentQuestIndex = 0;
$scope.currentQuestion = $scope.questions[$scope.currentQuestIndex];
And use the currentQuestion inside your view as:
<div>Question: {{currentQuestion.question}}</div>
When the user selects a correct answer, you could simply update the current question:
$scope.submitAnswer = function(){
// check if the answer is correct
if(isCorrect){
$scope.currentQuestion = $scope.questions[$scope.currentQuestIndex + 1];
}
}
This would dynamically update your view! Thanks to the data-binding feature provided by angularjs.
Related
I'm having a bit of an issue with Angular and selecting an item from a dropdown and making Angular update the model.
I've been searching through StackOverflow, but only with partial luck.
The problem is that when i'm manually setting a value on a model, my dropdown updates correct, but the model itself does not update;
$scope.setSelected = function(){
$scope.selected.id = 15;
//$scope.$apply();// $apply already in progress
}
From various answers on StackOverflow, I've found out that Angular does not know about this update and the suggested answer is to run either a $scope.$apply() or a $scope.$digest(), but both throw a $apply already in progress exception. I have a feeling that this is not the correct way for me to do it, since it doesn't make sense, that you have to trigger an event to select by value.
What is the correct way to select an item in a dropdown by a given value?
Full code example can be found at https://jsfiddle.net/c2x3jvut/
When clicking the "Select dinnerman" button, the dropdown updates correct, but the shown model and when clicking "Log selected" it only shows the selected model, but with an updated id.
You can use $filter to get whole object instead of each property.
$scope.setSelected = function(){
$scope.selected = $filter('filter')($scope.persons,15,'id')[0];
//$scope.selected.id = 15;
//$scope.$apply();// $apply already in progress
}
Here is the modified version
ng's select binds its value based on its ngModel, which is being manipulated incorrectly in the example. The correct method is to change the reference of the ngModel rather than the select's id:
$scope.setSelected = function () {
$scope.selected = $scope.persons[1]; // yes
// $scope.selected.id = 15; // no
};
Fiddle might have been updated to show the effects.
Not sure I understand your question, but still...
You may include in each entry of the dropdown:
...ng-click="setSelected(<value>)"...
and, of course, add a parameter to the function.
By the way, are you aware that in the fiddle example you are selecting ID 15 in function setSelected?
You are just updating the current model with an ID. But when you see carefully the name and age remains same. Further you need to update the whole object as below:
$scope.setSelected = function(){
$scope.selected.id = 15;
$scope.selected.name = "Dinner-man";
$scope.selected.age = 20;
}
Or anyother way to directly updates the object.
I have done a very small code on Plunkr under URL http://plnkr.co/wbVjBfpAA9WTpjEAs1GJ
I define first a Array Object which I display on a page with ng-repeat. No problem
Then on each item I add an Edit button and launch a function on ng-click
Now, I copy the selected array item into a new $scope.contractDetail and display this in the edit section (as input).
To my surprise when I start to edit the text in the input field, not only the $scope.contractDetail object gets updated but also the parent $scope.contracts.
I though I would, after edit to assign my $scontractDetail object specifically back into the $scope.contracts object at the given index.
Can somebody explain to me whta is happening here
thanks a lot
Copying your code from the plunkr to show:
angular.module('plunker', [])
.controller('MainCtrl', function($scope){
$scope.contracts = [{title: 'Master License Agreement'},{title: 'Limited Use Agreement'},{title: 'NDA'}];
$scope.editContract = function(indx){
$scope.contractDetail = $scope.contracts[indx];
}
})
Objects in JavaScript are essentially passed by reference, so when you set:
$scope.contractDetail = $scope.contracts[indx];
The two objects are the same object. When you begin to edit it, Android's dirty checking picks that up and shows the change in the other spot.
If you want to create a copy to edit (for an edit + save/cancel scenario) you can use angular.copy(obj) to create a duplicate that is not the same object.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
As an angularJS newbie, I am puzzled by the fact I have to bind to a function returned from the service to update the view, not the data itself, I couldn't find any official document explaining this, Does anyone know why?
JSFiddle Code Sample
<div ng-controller="MyCtrl">
binding to a function works!
<p ng-bind-html-unsafe="tempLog.returnBuffer()"></p>
<br><br>
bind to the service variable: Doesn't work, why?
<p>log={{tempLog.buffer}}</p>
<br><br>
bind to scope var instead of service var, still doesn't work
<p>log={{logBuffer}}</p>
bind to a scope var which points to the service Function, works!
<p>log={{pLogFunc()}}</p>
<button ng-click="addText('more')">Trace</button><br>
</div>
JS code
var myApp = angular.module('myApp',[]);
myApp.factory('myLog', function() {
var internalBuffer = "";
return {
buffer:internalBuffer,
trace:function(input){
internalBuffer = internalBuffer + "<br>" +input;
buff = input;
},
returnBuffer:function(){
return internalBuffer;
}
}
});
function MyCtrl($scope, myLog){
$scope.tempLog = myLog;
$scope.logBuffer = myLog.buffer;
$scope.pLogFunc = myLog.returnBuffer;
myLog.trace("aaa");
$scope.addText = function(str){
myLog.trace(str)
}
}
This is not an AngularJS binding problem, this is just how javascript works.
In your service:
1 buffer is assigned to a primitive variable internalBuffer.
2 trace() accepts a parameter that changes the internalBuffer primitive
3 returnBuffer() returns the internalBuffer primitive
Since trace() changes the internalBuffer primitive, any binding to buffer does not affect the changes in the internalBuffer, furthermore, returnBuffer() returns the value of the internalBuffer so naturally the changes you made with the trace() function affects the return value of the returnBuffer() function.
Any of these suggestions may work on your end:
[1] If you want to bind from the buffer property of your myLog service, then change your trace() function to something like this:
trace:function(input){
this.buffer = this.buffer + "<br>" +input;
}
[2] You may disregard the buffer property and solely use the returnBuffer() if youdon't want to expose yourinternalBufferand only use thetrace()to have access in changing theinternalBuffer`
[3] You can use both, the buffer property provides access to another buffer format while the internalBuffer holds all the private buffers / format / or anything else that you may not want to expose to the users of the service. Just be sure to update the buffer in your trace() function by using this as well.
Given something like this:
View = Backbone.Marionette.ItemView.extend({ });
myView = new View();
//region already exists
myLayout.region.show(myView)
//some time later this gets called again:
myLayout.region.show(myView)
I can see currentView in the docs but this only seems to apply at initialisation. Once a view is shown can I query the region to see the view? Either the view instance or type would be helpful. Looking in Chrome's debugger I can't see any properties/methods on the region that would help.
The motive for wanting to do this is so I don't show a static item view in a region again if it is already displayed as this can (especially if images are involved) cause a slight flickering effect on the screen.
Thanks
--Justin Wyllie
you can add a condition before calling show method:
if (myLayout.region.currentView != myView)
myLayout.region.show(myView)
so if you'll try to call show with the same View it wont be shown.
if you want to call region.show(myView) once you can check in this way:
if (_.isUndefined(myLayout.region.currentView))
myLayout.region.show(myView)
You can check the isClosed and $el attributes of the view. Something like
if (myView.isClosed || _.isUndefined(myView.$el)) {
myLayout.region.show(myView);
}
This is the same way the region checks to see if the view is closed or not:
show: function(view) {
this.ensureEl();
var isViewClosed = view.isClosed || _.isUndefined(view.$el);
...
I'm going out on a limb here and assuming that the OP's question is based on app behavior when navigating to different parts of the app via an anchor tag in the navigation or something similar.
This is how I found the question and I thought briefly that the answers would save my day. Although both answers so far are correct they do not quite solve the problem I was having. I wanted to display a persistent navigation bar. However, I did not want it to display on the login page. I was hopeful that detecting if a Region was already shown or not I'd be able to properly let the display logic take care of this.
As it turns out we were both on the right track to implement Regions as this provides granular control, but even after implementing the above I found that my nav bar would still "flicker" and essentially completely reload itself.
The answer is actually a bit ridiculous. Somehow in all the Backbone tutorials and research I've been doing the last two weeks I never came across the need to implement a javascript interface to interrupt normal link behavior. Whenever a navigation item was clicked the entire app was reloading. The routing was functioning so the content was correct, but the flicker was maddening.
I added the following to my app.js file right after the Backbone.history.start({pushState: true}); code:
// Holy crap this is SOOO important!
$(document).on("click", "a[href^='/']", function(event) {
if (!event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
event.preventDefault();
var url = $(event.currentTarget).attr("href").replace(/^\//, "");
Backbone.history.navigate(url, { trigger: true });
}
});
Check out this article for some explanation about the keyPress detection stuff. http://dev.tenfarms.com/posts/proper-link-handling
Boom! After adding this stuff in my app no longer completely reloads!
Disclaimer: I am very new to Backbone and the fact that the above was such a revelation for me makes me think that I may be doing something wrong elsewhere and this behavior should already exist in Backbone. If I've made a giant error here please comment and help me correct it.
Currently I run into the following problem,
we are working on a form heavy application and wanted a good user experience so we tried to stop have a save / update button everywhere.
Currently we try to $watch every Form Change, but this won't work correctly since it updates the scope when the model gets updated which causing problems on Decimal / Money Values.
What would you prefer? Still the messy save button or maybe something like Gmail did?
What are good methods to do this without save buttons.
/* EDITED */
Currently we use this method to update our form.
It first copys the scope in an object and checks if it is the same than the object that is set after the date got pulled.
$scope.$watch('task', function(scope) {
console.log($scope.updateForm);
scopeObject = angular.copy(scope);
if(scope !== undefined) {
if(!(_.isEqual(scopeObject, mainObject))){
//scope_copy.request_date = $filter('date')(new Date(scope.request_date), 'fullDate');
console.log('update');
scope.$update({pk: $routeParams.taskId}, function() {
scope.request_date = $filter('date')(scope.request_date);
mainObject = angular.copy(scope);
});
mainObject = angular.copy(scope);
}
}
}, true);
currently i think this code is somehow messy since it can't update decimal numbers.
but currently i don't have a better solution. (i don't want to use a Button to submit the form, it should be done interactivly).