Reload changed content created via ng-repeat without refreshing whole page - javascript

I'm currently trying to do the following, unfortunately without any success:
Basically, I have an array of objects, where for every object a button is created dynamically via the ng-repeat directive. When clicking a button, a new object is appended to the mentioned array (data will be sent to the server via api calls, the server will update the list internally and send the new array with the new object appended back to the view).
The same goes for deleting elements from that array.
Here's a little sample code of what I mean:
<span ng-repeat="x in allElements">
<button class="btn btn-primary" id="elementButtons">{{x.id}}</button>
</span>
There will be as many buttons as elements in $scope.allElements.
Then there's also this button, which basically causes the array to be reset to 0 elements:
<button id="clearAllElements" type="button" class="btn btn-danger"
data-toggle="button" ng-click="deleteAllElements()">Clear all</button>
The $scope.deleteAllElements() function calls the api to delete all elements from the server and fetches the new (empty) array from the server assigning it to $scope.allElements.
Now, how can I update the view without having to refresh the whole page such that only the created buttons are reloaded?
Thanks for any answers in advance,
a.j.stu
EDIT:
This is the function that is called when an element is to be added:
$scope.addElement = function(elementName) {
if ($scope.checkElementName(elementName)) {
var data = {"name": elementName.toUpperCase(),
"type": /*retrieve type of element from html element*/}
$http.post("api/addElement/", JSON.stringify(data))
.then(function(response) {
$scope.allElements = response.data; //the api call adds the element in the backend and returns an array with all elements appended the new one. This SHOULD refresh the view of all element buttons, but doesn't.
})
.catch(function(response) {
console.log("something went wrong adding element " + elementName);
})
.then(function(response) {
$('#newElementModal').modal('hide'); //#newElementModal is the modal that pops up when clicking a button to add a new element. here the name and type of the element are set and used in the api call above to add the element. Basically, when the modal hides, the view of all buttons should be refreshed.
});
} else {
console.log("invalid identifier of element.");
}
As far as I've understood, the .then() calls are asynchronous. But, if there are only .then() calls following the api call, this should not be a problem, right?

You should not have to worry about resfreshing the page. If your view is connected to the controller whose $scope is updated by the API calls adding and deleting elements, your view will adapt and display the new content.
For what it's worth, here's a snippet showing how it could work. Minus the API calls that add / delete data.
angular.module('dummyApp', [])
.controller('DummyController', function($scope) {
$scope.allElements = [
{ id : 1, name : "Joe"},
{ id : 2, name : "John"},
{ id : 3, name : "Jane"},
{ id : 4, name : "Alice"},
];
$scope.deleteAllElements = function () {
// deleting elements empties the $scope.allElements array
$scope.allElements.length = 0;
};
$scope.addElement = function () {
var element = {
id : generateId(),
name : 'whatever'
}
// new elements are pushed into the $scope.allElements array
$scope.allElements.push(element);
};
function generateId() {
return Math.floor(Math.random()*1000);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.js"></script>
<div ng-app="dummyApp" ng-controller="DummyController">
<span ng-repeat="x in allElements">
<button class="btn btn-primary" id="elementButtons">{{x.id}}</button>
</span>
<br/><br/>
<button id="clearAllElements" type="button" class="btn btn-danger"
data-toggle="button" ng-click="deleteAllElements()">Clear all</button>
<button id="clearAllElements" type="button" class="btn btn-danger"
data-toggle="button" ng-click="addElement()">Add</button>
</div>

use trackby x.id to update the view without refreshing the whole page

Just assign new response data from your server to $scope.allElements and it will be refreshed without reloading page.

Related

AngularJS - Get printed value from scope inside an attribute?

I'm currently working on an AngularJS project and I got stuck in this specific requirement.
We have a service that has all the data, DataFactoryService. Then, I have a controller called DataFactoryController that is making the magic and then plot it in the view.
<div ng-repeat = "list in collection">
{{list.name}}
...
</div>
Now, we have a requirement that pass multiple data into one element. I thought an "ng-repeat" would do, but we need to have it inside an element attribute.
The scenarios are:
At one of the pages, we have multiple lists with multiple data.
Each data has a unique code or ID that should be passed when we do an execution or button click.
There are instances that we're passing multiple data.
Something like this (if we have 3 items in a list or lists, so we're passing the 3 item codes of the list):
<a href = "#" class = "btn btn-primary" data-factory = "code1;code2;code3;">
Submit
</a>
<a href = "#" class = "btn btn-default" data-factory = "code1;code2;code3;">
Cancel
</a>
In the example above, code1,code2,code3 came from the list data. I tried several approach like "ng-repeat", "angular.each", array, "ng-model" but I got no success.
From all I've tried, I knew that "ng-model" is the most possible way to resolve my problem but I didn't know where to start. the code below didn't work though.
<span ng-model = "dataFactorySet.code">{{list.code}}</span>
{{dataFactorySet.code}}
The data is coming from the service, then being called in the controller, and being plot on the HTML page.
// Controller
$scope.list = dataFactoryService.getAllServices();
The data on the list are being loaded upon initialization and hoping to have the data tags initialized as well together with the list data.
The unique code(s) is/are part of the $scope.list.
// Sample JSON structure
[
{ // list level
name: 'My Docs',
debug: false,
contents: [ // list contents level
{
code: 'AHDV3128',
text: 'Directory of documents',
...
},
{
code: 'AHDV3155',
text: 'Directory of pictures',
...
},
],
....
},
{ // list level
name: 'My Features',
debug: false,
contents: [ // list contents level
{
code: 'AHGE5161',
text: 'Directory of documents',
...
},
{
code: 'AHGE1727',
text: 'Directory of pictures',
...
},
],
....
}
]
How can I do this?
PLUNKER -> http://plnkr.co/edit/Hb6bNi7hHbcFa9RtoaMU?p=preview
The solution for this particular problem could be writing 2 functions which will return the baseId and code with respect to the list in loop.
I would suggest to do it like below
Submit
Cancel
//inside your controller write the methods -
$scope.getDataFactory = function(list){
var factory = list.map( (a) => a.code );
factory = factory.join(";");
return factory;
}
$scope.getDataBase= function(list){
var base= list.map( (a) => a.baseId);
base= base.join(";");
return base;
}
Let me know if you see any issue in doing this. This will definitely solve your problem.
You don't really have to pass multiple data from UI if you are using Angular.
Two-way data binding is like blessing which is provided by Angular.
check your updated plunker here [http://plnkr.co/edit/mTzAIiMmiVzQfSkHGgoU?p=preview]1
What I have done here :
I assumed that there must be some unique id (I added Id in the list) in the list.
Pass that Id on click (ng-click) of Submit button.
You already have list in your controller and got the Id which item has been clicked, so you can easily fetch all the data of that Id from the list.
Hope this will help you... cheers.
So basing from Ashvin777's post. I came up with this solution in the Controller.
$scope.getFactoryData = function(list) {
var listData = list.contents;
listData = listData.map(function(i,j) {
return i.code;
});
return listData.join(';');
}

Dynamic attribute value element locator in Protractor

When I add a new button with some value it gets dynamically added into DOM. Non-Angular HTML element for this button is:
<li class="ui-state-default droppable ui-sortable-handle" id="element_98" data-value="2519">
25.19 EUR
<button type="button" class="btn btn-default removeParent">
<span class="glyphicon glyphicon-remove" aria-hidden="true">
</span>
</button>
</li>
Once I remove this button I want to check it is not present anymore. Element that I'm searching for is data-value="2519"and this could be anything I set, like for example 2000, 1000, 500, 1050,...
In page object file I have tried to use the following:
this.newValueButtonIsNotPresent = function(item) {
newValueButton = browser.element(by.id("containerQUICK_ADD_POINTS")).all(by.css('[data-value="' + item + '"]'));
return newValueButton.not.isPresent();
};
And in spec file I call this function as follows:
var twentyEurosButtonAttributeValue = '2000';
describe("....
it ("...
expect(predefined.newValueButtonIsNotPresent(twentyEurosButtonAttributeValue)).toBeTruthy();
I know this is not correct, but how I can achieve something like that or is there another way?
Stupid me, I found a simple solution. Instead dynamically locating an element I located the first on the list, which is always the one, which was newly added and then checked if it's text does not match:
Page object file:
this.newValueButtonIsNotPresent = function() {
newValueButton = browser.element(by.id("containerQUICK_ADD_POINTS")).all(by.tagName('li')).first();
return newValueButton.getText();
};
Spec file:
// verify element 20.00 EUR is not present
predefined.newValueButtonIsNotPresent().then(function(value) {
expect(value).not.toEqual(twentyEurosText);
});

How "Data" can help me

I'm doing a web game with javaScript and KnockoutJs library.
In my html file I have a array foreach, and the number that this array saves, is the same number of buttons that a I have to draw on the page. Like this:
<strong data-bind = "foreach: cena1.opcoes">
<button data-bind="click: $parent.teste">Opcão</button>
<font color="red"><strong data-bind="text: conteudo"> </strong></font><br>
What I want to know is, how will I know which button the player selected?
I put data like a parameter on my button function, but I don't know how this works. like this:
object.teste = function(data) {
}
You can call the function this way:
<button data-bind="click: function() { $parent.teste($data); }">Opcão</button>
or
<button data-bind="click: function() { $parent.teste($data/* here can be any arguments available in the current binding context */); }">Opcão</button>
Update
By default the first parameter is being passed to the click handler function is the current view model - $data in the current binding context.
For more details and advanced scenarios you can check the Knockout JS documentation.
The click binding passes the current item to the bound function.
var vm = {
items: [1, 2, 3],
click: function(data) {
alert('You clicked: ' + data);
}
};
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="foreach:items">
<button data-bind="click:$parent.click">Click</button>
</div>

Change scope value in a different view

Ok, I have struggeled a lot with this thing for some days now and I'm stuck. Here is what I want; I want to change my scope value in a different view by clicking on a button. So if I'm in the index.html view and click on a button I want to change the value of a scope on the index2.html view, and then display that view. Here is an example of my code which is not working
index.html
<div ng-controller="IndexController">
<button class="button button-block button-assertive" ng-click="checkValues()" value="checkitems" >
check values
</button>
</div>
IndexController.js
angular
.module('legeApp')
.controller('IndexController', function($scope, supersonic, $filter) {
$scope.checkValues = function(){
$scope.Diagnose = 'test';
var view = new supersonic.ui.View("legeApp#index2.html");
var customAnimation = supersonic.ui.animate("flipHorizontalFromLeft");
supersonic.ui.layers.push(view, { animation: customAnimation });
};
});
index2.html
<div ng-controller="IndexController">
<div class="card">
<div class="item item-text-wrap">
Test<b>{{Diagnose}} </b>
</div>
</div>
</div>
I can give the value outside the checkValues method, but I want it to be two different values depending on the button you click. Please help
I tried the code suggested, but I received an error. What am I doing wrong?
I try the code below and receive this error "SyntaxError: Unexpected token ':' - undefined:undefined". I also did not quite understand how and why I want to target the new value with supersonic.ui.views.params.current in the new view. I want to get the new value in the new view, not in a controller?Do I need two different controllers? I just want to update my values in a html view without being in it.
supersonic.ui.layers.push:
( view: view,
options: { params: {$scope.Diagnose : 'test'}
animation: customAnimation
}) => Promise
According to the supersonic push docs, the params attribute is meant for passing parameters between views:
JSON string of optional parameters to be passed to the target View,
accessible via supersonic.ui.views.params.current.
Try calling
supersonic.ui.layers.push: (
view: view,
options: {
params: {valueToBeSentAccrossView: <Your Value>}
animation: customAnimation
}) => Promise
and then retrieving the value in the target view using supersonic.ui.views.params.current.

Update list of divs after ajax post

I have this code that loads some div
<input class="input-large" placeholder="Notat" id="txtNewNote" type="text">
<button class="btn" id="btnAddNote" type="button">Lagre</button>
#foreach (var item in Model.UserNotes)
{
<div class="alert" id="divNote-#item.UserNoteID">
<button type="button" class="close" onclick="NoteDeleteClicked(#item.UserNoteID)" id="btnCloseNote">×</button>
<strong>#item.User.Name - #item.Added</strong><br />
#item.Text
</div>
}
Then I run this javascript when the user enter more text:
var Text = $("#txtNewNote").val();
var Added = Date.now();
vvar UserID = $("#hiddenUserID").text();
$.post("/api/apiUserNotes",{ Text : Text, Added: Added, UserID: UserID }, function() { //need functianality for updating the list of divs above });
Anyone can point me in the right direction here. I cannot just create the div after the post are done because I need to fetch data from the database so that the information in the div are correct.
There are mainly two approaches:
apiUserNotes returns HTML - This approach is a bit easier to maintain since you have only one template. But it is also more restricting in the sense that HTML is good for showing, but not so much for manipulating it.
apiUserNotes returns JSON - This is more flexible since a JSON API can be consumed by pretty much anything, from HTML to native iOS or Android apps, as it's much easier to manipulate. It's also more work though, as you then have templates both on the server-side (your Razor view) as well as on the client-side (your HTML/JavaScript).
This is more or less what #1 would look like:
You first make a partial view to display user notes:
_UserNotes.cshtml:
<div id="user-notes">
#foreach (var item in Model.UserNotes)
{
<div class="alert" id="divNote-#item.UserNoteID">
<button type="button" class="close" onclick="NoteDeleteClicked(#item.UserNoteID)" id="btnCloseNote">×</button>
<strong>#item.User.Name - #item.Added</strong><br />
#item.Text
</div>
}
</div>
Once you have this partial view, you can consume it from your view:
<input class="input-large" placeholder="Notat" id="txtNewNote" type="text">
<button class="btn" id="btnAddNote" type="button">Lagre</button>
#Html.Partial("_UserNotes")
And from your apiUserNotes action:
public PartialViewResult apiUserNotes(string text, DateTime added, int userID)
{
// TODO: save new note
var notes = yourRepo.GetUserNotes(userID);
return PartialView("_UserNotes", notes);
}
Finally, your client-side scripts simply overrites the div containing all user notes:
var data = {
text = $("#txtNewNote").val(),
added = Date.now(),
userID = $("#hiddenUserID").text()
};
$.post("/apiUserNotes", data, function (result) {
$('#user-notes').html(result);
});
There are of course much more efficient ways of doing this. For example, you don't need to reload all user notes; only the one your just added. But hopefully this gets you started.
You can access above Divs in following way, and update as per your requirement.
$.post("/api/apiUserNotes",{ Text : Text, Added: Added, UserID: UserID },
function()
{
//need functianality for updating the list of divs above
$("div.alert").each(....)
});

Categories