Update list of divs after ajax post - javascript

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(....)
});

Related

Reload Data with .sort from Dynamic Table with Jquery | JSON | Sort with New Data

I have been trying for a few days that when I use the .sort property the data is eliminated or modified instead of it being reloaded as new lines.
Attach Captures from the code Working
Image1 How work the code when i press the button, this sort to the highest price to lowest but how do you can see in the second image, the code appears up and this not delete the old data
Marked with "X" the data that does not have to show
this fragment is the one that generates the tables dynamically
const mostrarProductos = () => {
$.getJSON(URLJSON, (respuesta) => {
for (let z of respuesta) {
productosv2.push(z);
}
for (let x of productosv2) {
$("#fila").append(`
<tr class="deleteProductos">
<div class="card text-center" style="width: 18rem;" id='btnBorrarCarrito'>
<div class="card-body">
<input type="hidden" id="idProd" value="${x.id}"> </td>
<td class="card-title" id="${x.id}">${x.producto}</h2> </td>
<td class="card-text">$ ${x.precio}</p></td>
<div class="btn-group" role="group" aria-label="Basic mixed styles example">
<td><button type="button" class="btn btn-success" onclick="agregarCarrito(${x.id})">Agregar</button></td>
</div>
</div>
</div>
</tr>
`);
}
$("#fila").fadeIn("5000");
});
};
And this function is what orders them
function respuestaClickExpensive() {
$("#fila").html('');
let productosordenados = productosv2.sort((a, b) => {
if (a.precio > b.precio) {
return -1;
}
if (a.precio < b.precio) {
return 1;
}
return 0;
});
return productosordenados;
}
The one that orders them from smallest to largest is the same only that different signs and names.
As you can see I tried to use a ".html ("")" since previously in another cart attempt it used .innerHtml (Which does not work in this case either, Also the code of the cart is totally different from that moment that it worked for me
)
I tried the following:
$ ("#fila"). empty ();
Make another function to clean with .empty
Use Native JavaScript to generate the code.
$ ("#fila"). remove (); this removes all the content for me but does not regenerate it.
Change the HTML tag "Row" to a previous div which, since the div was not generated again, did not generate it again.
$ ("#fila tr"). remove ();
And some more things that I don't remember right now.
If you can guide me on what I did wrong or any suggestions to fix it, I appreciate it.
If I had to follow a template about posting on StackOverFlow or having chosen or named in a different way, I appreciate your comment since it is my first post
Project notes of possible relevance: The complete code outside of html and css is made with Native JavaScript, Jquery, Ajax, SASS and BootStrap.

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

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.

Adding JavaScript to MVC 4 .NET Bootstrap Web App

So I built this nice MVC 4 web app, I initially stayed away from fancy CSS and JS and just focused on getting it to work. I got to work, then I decide lets add bootstrap too it make it look pretty. Now I would like to add some basic java script. Some stuff like:
<button class="btn btn-default" type="button" onclick="javascript:document.getElementById('elementid').value=0;">Remove</button>
I have a List of parts, a part as a quantity, a number and name. So I loop through the list.
#foreach (Hardware hw in Model.HardwareList)
{
<div class="row">
<div class="col-lg-2">
#Html.EditorFor(h => hw.Quantity, "Int32")
</div>
<div class="col-lg-2">
#Html.DisplayFor(h => hw.Partnumber)
</div>
<div class="col-lg-8">
#Html.DisplayFor(h => hw.PartName)
</div>
</div>
}
My custom editor, is textbox with some bootstrap css to make look pretty. This generates nice simple view but all of my text boxes have a nameof "hw.Quantity" and id of "hw_Quantity". So I can't use getElementById(), if I change the id the textboxes when I go to post the model, I get null list. I feel like I must be missing something super simple.
editor template
#model int?
#{
int i;
if (!Model.HasValue)
{
i = 0;
}
else
{
i = Model.Value;
}
var htmlAttributes = new RouteValueDictionary();
if (ViewBag.#class != null)
{
htmlAttributes.Add("class", "form-control " + ViewBag.#class);
}
else
{
htmlAttributes.Add("class", "form-control");
}
if (ViewBag.placeholder != null)
{
htmlAttributes.Add("placeholder", ViewBag.placeholder);
}
}
<div class="form-group#(Html.ValidationErrorFor(m => m, " has-error")) input-group">
#Html.TextBox(
"",
ViewData.TemplateInfo.FormattedModelValue,
htmlAttributes)
#Html.ValidationMessageFor(m => m, null, new { #class = "help-block" })
</div>
I'm assuming you have the remove button for every row, on click of that button you can execute a javascript function that can look into the clicked button's parent ".row" and change the "hw.Quantity" value in that row. Like
onclick="javascript:$(this).parents('.row').find('elementid').val(0);"
Additionally you can use Html's data- attributes to store additional data..
For each row add unique "data-" attribute , maybe row id. I am sure you have to have an Id per a row.
htmlAttributes('data-id', {rowid})
You will have to pass this rowId to your custom editor.
Than If you have a remove or clear button per row you can add simple javascript on it
<button class="btn btn-default" type="button" onclick="$('[data-id=#rowId]').val(0)">Remove</button>

Data binding while manipulating the DOM not working with Angular, Knockout

I am looking to implement Angular, Knockout or another to data-bind a wizard-style application form proof-of-concept (no server-side code). However I appear to be breaking the data bindings when I clone the data-bound div.
The first few steps of the application form capture data, while the later steps present the data back to the user to allow them confirm what was entered. I am manipulating the DOM by inserting the appropriate step when 'next' is pressed and taking out the last step when 'previous' is pressed. I do this using detatch, clone and remove.
Can anyone give advise on the approach they would take to make this work, and what frameworks I should look at?
Below is pseudocode to give an idea of the structure. The pseudo-data-binding-code is just how I thought it would work, I'm not bedded to any framework.
HTML View
<div id="wizard">
<div id="step1">Enter your name: <input type="text" id="name" /></div>
</div>
<div id="actions"><input type="button" value="Previous" /><input type="button" value="Next" onClick="goNext();" /></div>
<div id="steps">
<div id="stepA">Enter your age: <input type="text" id="age" databind="theAge" /></div>
<div id="stepB">The age you entered - {{ theAge }} is too young!</div>
<div id="stepC">Enter your favourite colour: <input type="text" id="faveColour" databind="faveCol" /></div>
<div id="stepD">Hi {{ name }}. You are {{ age }} years old and your favourite colour is {{ faveCol }}</div>
</div>
JavaScript
<script>
function goNext() {
// figure out which step is next
insertStepIntoWizard(step, index, title);
}
function insertStepIntoWizard(step, index, title) {
var element = step.detach();
wizard.steps('insert', index, {
title: title,
content: element.clone()
});
console.log('insertStepIntoWizard - just inserted ' + step.attr('id') + ' into wizard position ' + index);
}
</script>
I think you're thinking in terms of having your view reflect your entity model, which is basically templating. If you're looking to use Knockout/Angular, consider using it's view model to manage page state / flow / actions, in addition to controlling the entity. (Writing jQuery code to poke about with the DOM and clone, show/hide, etc is no fun). #sabithpocker makes a similar point.
Working example: I'm familiar with Knockout, and have created an example jsFiddle of your scenario: http://jsfiddle.net/overflew/BfRq8/5/
Notes:
I've used the template tag to house each section of the wizard, and all steps point to the same model/entity within the viewmodel.
To emphasise what's happening with the publish/subscribe nature of the bindings:
The user input is also relayed at the bottom of the page.
The form title is dynamic, as well as the 'step'
ko.computed is used to 'compute' the full name and if there are any 'steps' left to go
Knockout-specific: Notice the occurrence of brackets popping up around the place. This is one thing that may catch you out occasionally if you choose to learn Knockout. It just means that you're evaluating the binding container to get the value.
View model
<div>
<h3 data-bind="text: currentStep().name"></h3>
<div data-bind="template: { name: 'wizard-step1' }, visible: currentStep().id === 0"></div>
<div data-bind="template: { name: 'wizard-step2' }, visible: currentStep().id === 1"></div>
<div data-bind="template: { name: 'wizard-step3' }, visible: currentStep().id === 2"></div>
<input type="button" value="Next step" data-bind="click: onNext, visible: hasNextSteps" />
<input type="button" value="Submit" data-bind="click: onSubmit,visible: !hasNextSteps()" />
<div data-bind="visible: submitResultMessage, text: submitResultMessage"></div>
</div>
<div>
<h3>Your inputs</h3>
<div data-bind="visible: questions.fullName">Full name: <span data-bind="text: questions.fullName"></span></div>
<div data-bind="visible: questions.age">Age: <span data-bind="text: questions.age"></span>
</div>
<div data-bind="visible: questions.favouriteColour">Favourite colour: <span data-bind="text: questions.favouriteColour"></span>
</div>
</div>
<script type="text/html" id="wizard-step1">
<div>
First name: <input data-bind="value: questions.firstName, valueUpdate:'afterkeydown'" />
</div>
<div>
Last name: <input data-bind="value: questions.lastName, valueUpdate:'afterkeydown'" />
</div>
</script>
<script type="text/html" id="wizard-step2">
<div>
Age: <input data-bind="value: questions.age, valueUpdate:'afterkeydown'" />
</div>
</script>
<script type="text/html" id="wizard-step3">
<div>
Favourite colour: <input data-bind="value: questions.favouriteColour, valueUpdate:'afterkeydown'" />
</div>
</script>
View
// Entity for holding form data.
var FormData = function() {
var self = this;
self.firstName = ko.observable("");
self.lastName = ko.observable("");
self.age = ko.observable("");
self.favouriteColour = ko.observable("");
self.fullName = ko.computed(function() {
if (!self.firstName() && !self.lastName()) {
return "";
}
return self.firstName() + " " + self.lastName();
});
}
// Quick handling for managing steps of the wizard
var wizardSteps = [
{ id: 0, name: "Wizard step 1"},
{ id: 1, name: "More questions"},
{ id: 2, name: "Last step"}
];
var ViewModel = function() {
var self = this;
// Properties
self.questions = new FormData();
self.currentStep = ko.observable(wizardSteps[0]);
self.submitResultMessage = ko.observable();
// Actions
self.onNext = function() {
var currentIndex = self.currentStep().id;
if (self.hasNextSteps()) {
// Move forward one step on the wizard
self.currentStep(wizardSteps[currentIndex + 1]);
}
};
self.onSubmit = function() {
self.submitResultMessage("Data is now submitted ");
};
// Page control
self.hasNextSteps = ko.computed(function() {
var currentIndex = self.currentStep().id;
return currentIndex < wizardSteps.length - 1;
});
};
ko.applyBindings(new ViewModel());
What most of the bleeding edge javascript frameworks is trying to do is attempting to manage the huge amount of code in current applications where large amount of business logic is implemented with javascript in client side. So they are mostly trying to provide you with some architecture to organize your code to make it easy to manage, read and scale.
In your case you are trying to neglect the architecture like MVC or MVWhatever they are providing and use the framework to do some templating, If I understand you correctly. For that you can better get the help of some templating engine in javascript like handlebars and use it to manually render your data stored in your current javascript app.
see it here http://handlebarsjs.com/
Simply re-initialize your bindings.
This solves my problem when using knockout.
When you change the main DOM element which is actually being referenced for MVVM, it turn-out that any change or re-structuring that element hampers the dom relationship.
Eventually, though the placement of the item looks same but technically, in java script engine it is not. So the problem occurs.
In order to fix something like this, simply have a constructor which will initialize or re-initialize the mapping for a particular element in DOM.
Hope that solves your problem.
Example:
function ReInitializeVM()
{
ko.cleanNode(document.getElementById("userDetails"));
ko.applyBindings(viewModel, document.getElementById("userDetails"));
}
The function can be called everytime where you feel the need of re-initializing the bindings. Calling the function will remove any binding on the element with id "userDetails" and then the applyBindings can be called to initialize the binding. This will invoke bindings for any new/changed element.

I have two divs with the same ng-controller in AngularJS, how can I make them share information?

I have two different div tags in my html code referencing the same controller in AngularJS. What I suspect is that since these divs aren't nested they each have their own instance of the controller, thus the data is different in both.
<div ng-controller="AlertCtrl">
<ul>
<li ng-repeat="alert in alerts">
<div class="span4">{{alert.msg}}</div>
</li>
</ul>
</div>
<div ng-controller="AlertCtrl">
<form ng-submit="addAlert()">
<button type="submit" class="btn">Add Alert</button>
</form>
</div>
I know this could easily be fixed by including the button in the first div but I feel this is a really clean and simple example to convey what I am trying to achieve. If we were to push the button and add another object to our alerts array the change will not be reflected in the first div.
function AlertCtrl($scope) {
$scope.alerts = [{
type: 'error',
msg: 'Oh snap! Change a few things up and try submitting again.'
}, {
type: 'success',
msg: 'Well done! You successfully read this important alert message.'
}];
$scope.addAlert = function() {
$scope.alerts.push({
type: 'sucess',
msg: "Another alert!"
});
};
}
This is a very common question. Seems that the best way is to create a service/value and share between then.
mod.service('yourService', function() {
this.sharedInfo= 'default value';
});
function AlertCtrl($scope, yourService) {
$scope.changeSomething = function() {
yourService.sharedInfo = 'another value from one of the controllers';
}
$scope.getValue = function() {
return yourService.sharedInfo;
}
}
<div ng-controller="AlertCtrl">{{getValue()}}</div>
<div ng-controller="AlertCtrl">{{getValue()}}</div>
If I understand the question correctly, you want to sync two html areas with the same controller, keeping data synced.
since these divs aren't nested they each have their own instance of the controller, thus the data is different in both
This isn't true, if you declare the controllers with the same alias (I'm using more recente angular version):
<div ng-controller="AlertCtrl as instance">
{{instance.someVar}}
</div>
<div ng-controller="AlertCtrl as instance">
{{instance.someVar}} (this will be the same as above)
</div>
However, if you WANT them to be different and comunicate each other, you will have to declare different aliases:
<div ng-controller="AlertCtrl as instance1">
{{instance1.someVar}}
</div>
<div ng-controller="AlertCtrl as instance2">
{{instance2.someVar}} (this will not necessarily be the same as above)
</div>
Then you can use services or broadcasts to comunicate between them (the second should be avoided, tough).

Categories