Meanjs built in app, how to update USER array from articles module - node.js

I'm using the Articles and Users implementation into Meanjs built-in app.
List of articles (View) with the button for every item:
<a data-ng-controller="MyArticlesController" data-ng-repeat="article in articles" class="list-group-item">
<button data-ng-click="addArt2me()" class="glyphicon glyphicon-plus addArt2me" ></button>
<h4 class="list-group-item-heading" data-ng-bind="article.title"></h4>
<p> {{article.content | limitTo:140}} </p>
</a>
And here the controller with the trigger function $scope.addArt2me():
'use strict';
angular.module('users').controller('MyArticlesController', ['$scope', 'Articles', 'Users', '$location',
function($scope, Articles, Users, $location) {
var myUser = new Users($scope.user);
$scope.addArt2me = function() {
var myArticle = new Articles($scope.article);
myUser.userArticles.push(myArticle._id);
myUser.$update(function(response) {
console.log("Actualize!! con : " + user.userArticles.length + "__" + response);
}, function(errorResponse) {
console.log("updatError: " + errorResponse);
$scope.error = errorResponse;
});
}
}
]);
In the user's model I've an array of articles._id userArticles.
The View renders a list of articles with a button that triggers the function addArt2me() in the controller, which pushes and updates the myArticle._id inside ´userArticles´.
It works successfully and saves the element into DB :)
console: Actualize!! con : 60__[object Object]
...but only the first time, the next time it triggers an error :(
PUT http://localhost:3000/users 400 (Bad Request)
updatError: [object Object]´
Do I need to deal with some kind of service to update the USERS module being in ARTICLES module?
Can't I just update the model of the user with Mongoose?
Why does it works well for the first saved article ?
Any guide is very appreciated. Thanks!

I just founded, after 2 day of looking for it.
Needed to declare the User Model, outside the subcontroller because it iterates for every article.
so move ´$scope.myUser = new Users($scope.user);´ into ´ArticlesController´ Did the job!
Thanks for your time!

Related

handling JSOM clientcontext properly

I am trying out JSOM in Sharepoint 2016. I have made a WebPart containing the following code -
<div id="user-output"></div>
Movie Title: <input type="text" id="movie-title" /><br />
Description: <input type="text" id="movie-description" /><br />
<button type="button" id="submit-button">Add Movie</button>
<div id="movies-output"></div>
<script type="text/javascript" src="/SiteAssets/jquery-3.2.1.min.js"></script>
<script type="text/ecmascript" src="../_layouts/15/SP.UserProfiles.js"></script>
<script type="text/javascript">
$(function () {
$('#submit-button').on('click', function () {
var context = SP.ClientContext.get_current();
var movies = context.get_web().get_lists().getByTitle('Movies');
var movieCreationInfo = new SP.ListItemCreationInformation();
var movie = movies.addItem(movieCreationInfo);
movie.set_item("Title", $('#movie-title').val());
movie.set_item("MovieDescription", $('#movie-description').val());
movie.update();
context.load(movie);
context.executeQueryAsync(success, failure);
});
function success() {
$('#movies-output').text('Created movie!');
}
function failure() {
$('#movies-output').text('Something went wrong');
}
var upp;
// Ensure that the SP.UserProfiles.js file is loaded before the custom code runs.
//SP.SOD.executeOrDelayUntilScriptLoaded(getUserProperties, 'SP.UserProfiles.js');
SP.SOD.executeFunc('userprofile', 'SP.UserProfiles.PeopleManager', getUserProperties);
//SP.SOD.executeFunc('SP.UserProfiles.js', getUserProperties);
function getUserProperties() {
// Get the current client context and PeopleManager instance.
var clientContext = new SP.ClientContext.get_current();
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
upp = peopleManager.getMyProperties();
clientContext.load(upp, 'UserProfileProperties');
clientContext.executeQueryAsync(onRequestSuccess, onRequestFail);
}
// This function runs if the executeQueryAsync call succeeds.
function onRequestSuccess() {
$('#user-output').html('User Name: ' + upp.get_userProfileProperties()['PreferredName'] +
'<br/>Department: ' + upp.get_userProfileProperties()['Department'] +
'<br/>Designation: ' + upp.get_userProfileProperties()['Title'] +
'<br/>Employee ID: ' + upp.get_userProfileProperties()['EmployeeID'] +
'<br/>Branch Code: ' + upp.get_userProfileProperties()['branchCode']
);
}
// This function runs if the executeQueryAsync call fails.
function onRequestFail(sender, args) {
$('#user-output').text("Error: " + args.get_message());
}
});
What this code does is -
Show user information in user-output div at document load ready
Saves a movie record when Add Movie button is clicked
However, for some reason, when Add Movie button is clicked, the code adds two movies instead of one. I think it has something to do with the ClientContext. But I am not sure why, or how to solve it. Can anyone help?
I am not sure how it happened, or if it's a bug, but while fiddling with the page source to find out why double posting was occurring, I saw that my web part code was being rendered twice in the page. One part was visible, and another was under a hidden div. However, when I went to edit page to delete the hidden web part, I couldn't. So I restored my page to the template version and re-added the web part. After that the web part was working correctly. There were no problems with the code.

Rendering data in handlebars using express framework

Hello I am working in Express Framework, I am using handlebars to render the data from my mysql table. While trying to render the data using below code,instead of rendering value it displaying [object object]. I posted my code below.
index.js:
var query = connection.query('SELECT * FROM requestor_auth WHERE question_id = ? AND answer = ? AND app_key = ? LIMIT 1', [data.qid, data.ansvalue, data.appid], function(err,rows)
{
if(err) {
console.log("Error Selecting : %s ",err );
res.redirect('/');
} else {
res.render('requestform',{page_title:"Edit Customers - Node.js",data:rows});
}
requestform.hbs:
<div class="addressto">
<h4>To,</h4>
<br>
<span style="font-size:18px;margin-left:10px;">The Collector Of</span>
<input type="text" value="{{data}}" class="line" class="text-line" style="margin-left:35px;"><br>
</div>
The value in the form input displaying as [object object]. I tried as data.key_value to render the data but it is not displaying the value.
Please give me a solution. Thank you.
Because the result of Mysql response is array so it should be:
var query = connection.query('SELECT * FROM requestor_auth WHERE question_id = ? AND answer = ? AND app_key = ? LIMIT 1', [data.qid, data.ansvalue, data.appid], function(err,rows) {
if(err) {
console.log("Error Selecting : %s ",err );
res.redirect('/');
} else {
res.render('requestform',{page_title:"Edit Customers - Node.js",data:rows[0]});
}
If there's a same error you should console.log() your result to check the value.
The rows argument in your callback function is by a select query always an array of objects. With handlebars you should be able to do the following:
<div class="addressto">
<h4>To,</h4>
<br>
<span style="font-size:18px;margin-left:10px;">The Collector Of</span>
<input type="text" value="{{data[0].answer}}" class="line text-line" style="margin-left:35px;">
<br>
</div>
Also multiple class names can be in one class attribute.

How call a function when view is attached in Durandal?

I'm just started to develeop a web app with Durandal. I don't understand how call a function from a viewmodel and why if I find an element of my document it seems is not attached yet.
Example: viewmodel.js
define( ['libone', 'libtwo'], function () {
$('.carousel').libone({
expandbuttons: true,
keyboard: true,
mouse: true
});
});
It doesn't find the ID call carousel is why there's no view.hmtl content but index.html content.
Any ideas?
Thanks in advance
UPDATE
No errors but the view content is not returned.
view.html
<section>
<h2 data-bind="html:name"></h2>
<blockquote data-bind="html:descr"></blockquote>
<div class="carousel">
<div class="carousel-sections">
<div class="carousel-section"> ... some content ... </div>
</div>
</div>
<a id="carousel-scroll-prev" href="#"></a>
<a id="carousel-scroll-next" href="#"></a>
<section>
modelview.js
define( ['libone', 'libtwo'], function (libone, libtwo) {
var viewattached = function(view){
var view = $(view);
view.find('.carousel').libone({
expandbuttons: true,
keyboard: true,
mouse: true
});
};
var vm = {
attached: viewattached,
name: 'How about we start?',
descr: 'You have many choices to make and many roads to cross...'
};
return vm;
});
Only name, descr and scroll are shown but not carousel-section.
The rendering problem has been resolved using compositionComplete instead attached.
to gain access to the controls using jquery like you are requesting, you should use the views attached event
e.g.
define( ['libone', 'libtwo'], function (libone, libtwo) {
var viewattached = function(view){
var view = $(view);
view.find('.carousel').libone({
expandbuttons: true,
keyboard: true,
mouse: true
});
};
var vm = {
attached: viewattached
};
return vm;
});
The other one that may work will be the compsitionComplete..
compositionComplete works fine.But if you refresh the page ,composition complete skips the binding of element with the carousel and teh carousel doesnt work.
Any

Create jqueryMobile page dynamically based on the url

I am creating an application to get some experience in jQuery Mobile and backbone. I have made a "restful" API with node.js that handles the data I need. It works fine with all my static pages I made in index.html. But when I need to create a page with data from a certain id I am a bit lost.
For example when I want to display all items(/items) I have a data-role=page with id items that list all items, but when I need to go to a detailed page for each item (/items/1) i want to create that details page whenever a user wants details on an item, in other words when a user visit the url spots#3 for example.
Is this possible?
my router: the model gives me all data i want
Spoter.Router = Backbone.Router.extend({
routes: {
"": "",
"spot#:id": "spotDetails"
},
//Details on a certain spot with id
spotDetails: function(id) {
var spotDetailsContentDiv = Spoter.spotDetailsContent;
spotDetailsContentDiv.empty();
var spot = new Spoter.spotModel({id: id});
spot.fetch({
successCallback: function(data) {
var spotDetailsView = new Spoter.spotDetailsView({
model: data
});
spotDetailsContentDiv.html(spotDetailsView.render().el);
}
});
}
});
View:
Spoter.spotDetailsView = Backbone.View.extend({
render:function () {
this.$el.html(this.template(this.model));
return this;
}
});
Template with underscore
<ul data-role="listview" data-theme="c" data-inset="true">
<li>
<a href="#">
<h1><%= this.model.name %></h1>
<p><%= this.model.description %></p>
</a>
</li>
</ul>

How to put a delay on AngularJS instant search?

I have a performance issue that I can't seem to address. I have an instant search but it's somewhat laggy, since it starts searching on each keyup().
JS:
var App = angular.module('App', []);
App.controller('DisplayController', function($scope, $http) {
$http.get('data.json').then(function(result){
$scope.entries = result.data;
});
});
HTML:
<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:searchText">
<span>{{entry.content}}</span>
</div>
The JSON data isn't even that large, 300KB only, I think what I need to accomplish is to put a delay of ~1 sec on the search to wait for the user to finish typing, instead of performing the action on each keystroke. AngularJS does this internally, and after reading docs and other topics on here I couldn't find a specific answer.
I would appreciate any pointers on how I can delay the instant search.
UPDATE
Now it's easier than ever (Angular 1.3), just add a debounce option on the model.
<input type="text" ng-model="searchStr" ng-model-options="{debounce: 1000}">
Updated plunker:
http://plnkr.co/edit/4V13gK
Documentation on ngModelOptions:
https://docs.angularjs.org/api/ng/directive/ngModelOptions
Old method:
Here's another method with no dependencies beyond angular itself.
You need set a timeout and compare your current string with the past version, if both are the same then it performs the search.
$scope.$watch('searchStr', function (tmpStr)
{
if (!tmpStr || tmpStr.length == 0)
return 0;
$timeout(function() {
// if searchStr is still the same..
// go ahead and retrieve the data
if (tmpStr === $scope.searchStr)
{
$http.get('//echo.jsontest.com/res/'+ tmpStr).success(function(data) {
// update the textarea
$scope.responseData = data.res;
});
}
}, 1000);
});
and this goes into your view:
<input type="text" data-ng-model="searchStr">
<textarea> {{responseData}} </textarea>
The mandatory plunker:
http://plnkr.co/dAPmwf
(See answer below for a Angular 1.3 solution.)
The issue here is that the search will execute every time the model changes, which is every keyup action on an input.
There would be cleaner ways to do this, but probably the easiest way would be to switch the binding so that you have a $scope property defined inside your Controller on which your filter operates. That way you can control how frequently that $scope variable is updated. Something like this:
JS:
var App = angular.module('App', []);
App.controller('DisplayController', function($scope, $http, $timeout) {
$http.get('data.json').then(function(result){
$scope.entries = result.data;
});
// This is what you will bind the filter to
$scope.filterText = '';
// Instantiate these variables outside the watch
var tempFilterText = '',
filterTextTimeout;
$scope.$watch('searchText', function (val) {
if (filterTextTimeout) $timeout.cancel(filterTextTimeout);
tempFilterText = val;
filterTextTimeout = $timeout(function() {
$scope.filterText = tempFilterText;
}, 250); // delay 250 ms
})
});
HTML:
<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:filterText">
<span>{{entry.content}}</span>
</div>
In Angular 1.3 I would do this:
HTML:
<input ng-model="msg" ng-model-options="{debounce: 1000}">
Controller:
$scope.$watch('variableName', function(nVal, oVal) {
if (nVal !== oVal) {
myDebouncedFunction();
}
});
Basically you're telling angular to run myDebouncedFunction(), when the the msg scope variable changes. The attribute ng-model-options="{debounce: 1000}" makes sure that msg can only update once a second.
<input type="text"
ng-model ="criteria.searchtext""
ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
class="form-control"
placeholder="Search" >
Now we can set ng-model-options debounce with time and when blur, model need to be changed immediately otherwise on save it will have older value if delay is not completed.
For those who uses keyup/keydown in the HTML markup.
This doesn't uses watch.
JS
app.controller('SearchCtrl', function ($scope, $http, $timeout) {
var promise = '';
$scope.search = function() {
if(promise){
$timeout.cancel(promise);
}
promise = $timeout(function() {
//ajax call goes here..
},2000);
};
});
HTML
<input type="search" autocomplete="off" ng-model="keywords" ng-keyup="search()" placeholder="Search...">
Debounced / throttled model updates for angularjs : http://jsfiddle.net/lgersman/vPsGb/3/
In your case there is nothing more to do than using the directive in the jsfiddle code like this:
<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-ampere-debounce
/>
Its basically a small piece of code consisting of a single angular directive named "ng-ampere-debounce" utilizing http://benalman.com/projects/jquery-throttle-debounce-plugin/ which can be attached to any dom element. The directive reorders the attached event handlers so that it can control when to throttle events.
You can use it for throttling/debouncing
* model angular updates
* angular event handler ng-[event]
* jquery event handlers
Have a look : http://jsfiddle.net/lgersman/vPsGb/3/
The directive will be part of the Orangevolt Ampere framework (https://github.com/lgersman/jquery.orangevolt-ampere).
Just for users redirected here:
As introduced in Angular 1.3 you can use ng-model-options attribute:
<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-model-options="{ debounce: 250 }"
/>
I believe that the best way to solve this problem is by using Ben Alman's plugin jQuery throttle / debounce. In my opinion there is no need to delay the events of every single field in your form.
Just wrap your $scope.$watch handling function in $.debounce like this:
$scope.$watch("searchText", $.debounce(1000, function() {
console.log($scope.searchText);
}), true);
Another solution is to add a delay functionality to model update. The simple directive seems to do a trick:
app.directive('delayedModel', function() {
return {
scope: {
model: '=delayedModel'
},
link: function(scope, element, attrs) {
element.val(scope.model);
scope.$watch('model', function(newVal, oldVal) {
if (newVal !== oldVal) {
element.val(scope.model);
}
});
var timeout;
element.on('keyup paste search', function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
scope.model = element[0].value;
element.val(scope.model);
scope.$apply();
}, attrs.delay || 500);
});
}
};
});
Usage:
<input delayed-model="searchText" data-delay="500" id="searchText" type="search" placeholder="live search..." />
So you just use delayed-model in place of ng-model and define desired data-delay.
Demo: http://plnkr.co/edit/OmB4C3jtUD2Wjq5kzTSU?p=preview
I solved this problem with a directive that basicly what it does is to bind the real ng-model on a special attribute which I watch in the directive, then using a debounce service I update my directive attribute, so the user watch on the variable that he bind to debounce-model instead of ng-model.
.directive('debounceDelay', function ($compile, $debounce) {
return {
replace: false,
scope: {
debounceModel: '='
},
link: function (scope, element, attr) {
var delay= attr.debounceDelay;
var applyFunc = function () {
scope.debounceModel = scope.model;
}
scope.model = scope.debounceModel;
scope.$watch('model', function(){
$debounce(applyFunc, delay);
});
attr.$set('ngModel', 'model');
element.removeAttr('debounce-delay'); // so the next $compile won't run it again!
$compile(element)(scope);
}
};
});
Usage:
<input type="text" debounce-delay="1000" debounce-model="search"></input>
And in the controller :
$scope.search = "";
$scope.$watch('search', function (newVal, oldVal) {
if(newVal === oldVal){
return;
}else{ //do something meaningful }
Demo in jsfiddle: http://jsfiddle.net/6K7Kd/37/
the $debounce service can be found here: http://jsfiddle.net/Warspawn/6K7Kd/
Inspired by eventuallyBind directive http://jsfiddle.net/fctZH/12/
Angular 1.3 will have ng-model-options debounce, but until then, you have to use a timer like Josue Ibarra said. However, in his code he launches a timer on every key press. Also, he is using setTimeout, when in Angular one has to use $timeout or use $apply at the end of setTimeout.
Why does everyone wants to use watch? You could also use a function:
var tempArticleSearchTerm;
$scope.lookupArticle = function (val) {
tempArticleSearchTerm = val;
$timeout(function () {
if (val == tempArticleSearchTerm) {
//function you want to execute after 250ms, if the value as changed
}
}, 250);
};
I think the easiest way here is to preload the json or load it once on$dirty and then the filter search will take care of the rest. This'll save you the extra http calls and its much faster with preloaded data. Memory will hurt, but its worth it.

Resources